omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev484__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/.omlish-manifests.json +12 -0
- omlish/__about__.py +15 -15
- omlish/argparse/all.py +17 -9
- omlish/argparse/cli.py +16 -3
- omlish/argparse/utils.py +21 -0
- omlish/asyncs/asyncio/rlock.py +110 -0
- omlish/asyncs/asyncio/sync.py +43 -0
- omlish/asyncs/asyncio/utils.py +2 -0
- omlish/asyncs/sync.py +25 -0
- omlish/bootstrap/_marshal.py +1 -1
- omlish/bootstrap/diag.py +12 -21
- omlish/bootstrap/main.py +2 -5
- omlish/bootstrap/sys.py +27 -28
- omlish/cexts/__init__.py +0 -0
- omlish/cexts/include/omlish/omlish.hh +1 -0
- omlish/collections/__init__.py +13 -1
- omlish/collections/attrregistry.py +210 -0
- omlish/collections/cache/impl.py +1 -0
- omlish/collections/identity.py +1 -0
- omlish/collections/mappings.py +28 -0
- omlish/collections/trie.py +5 -1
- omlish/collections/utils.py +77 -0
- omlish/concurrent/all.py +2 -1
- omlish/concurrent/futures.py +25 -0
- omlish/concurrent/threadlets.py +1 -1
- omlish/daemons/reparent.py +2 -3
- omlish/daemons/spawning.py +2 -3
- omlish/dataclasses/__init__.py +2 -0
- omlish/dataclasses/impl/api/classes/decorator.py +3 -0
- omlish/dataclasses/impl/api/classes/make.py +3 -0
- omlish/dataclasses/impl/concerns/repr.py +15 -2
- omlish/dataclasses/impl/configs.py +97 -37
- omlish/dataclasses/impl/generation/compilation.py +21 -19
- omlish/dataclasses/impl/generation/globals.py +1 -0
- omlish/dataclasses/impl/generation/ops.py +1 -0
- omlish/dataclasses/impl/generation/processor.py +105 -24
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +8 -8
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/cmds/__init__.py +0 -0
- omlish/diag/{lslocks.py → cmds/lslocks.py} +6 -6
- omlish/diag/{lsof.py → cmds/lsof.py} +6 -6
- omlish/diag/{ps.py → cmds/ps.py} +6 -6
- omlish/diag/pycharm.py +16 -2
- omlish/diag/pydevd.py +58 -40
- omlish/diag/replserver/console.py +1 -1
- omlish/dispatch/__init__.py +18 -12
- omlish/dispatch/methods.py +50 -140
- omlish/dom/rendering.py +1 -1
- omlish/formats/dotenv.py +1 -1
- omlish/formats/json/stream/__init__.py +13 -0
- omlish/funcs/guard.py +225 -0
- omlish/graphs/dot/rendering.py +1 -1
- omlish/http/all.py +44 -4
- omlish/http/clients/asyncs.py +33 -27
- omlish/http/clients/base.py +17 -1
- omlish/http/clients/coro/__init__.py +0 -0
- omlish/http/clients/coro/sync.py +171 -0
- omlish/http/clients/default.py +208 -29
- omlish/http/clients/executor.py +56 -0
- omlish/http/clients/httpx.py +72 -11
- omlish/http/clients/middleware.py +181 -0
- omlish/http/clients/sync.py +33 -27
- omlish/http/clients/syncasync.py +49 -0
- omlish/http/clients/urllib.py +6 -3
- omlish/http/coro/client/connection.py +15 -6
- omlish/http/coro/io.py +2 -0
- omlish/http/flasky/__init__.py +40 -0
- omlish/http/flasky/_compat.py +2 -0
- omlish/http/flasky/api.py +82 -0
- omlish/http/flasky/app.py +203 -0
- omlish/http/flasky/cvs.py +59 -0
- omlish/http/flasky/requests.py +20 -0
- omlish/http/flasky/responses.py +23 -0
- omlish/http/flasky/routes.py +23 -0
- omlish/http/flasky/types.py +15 -0
- omlish/http/urls.py +67 -0
- omlish/inject/__init__.py +38 -18
- omlish/inject/_dataclasses.py +4986 -0
- omlish/inject/binder.py +4 -48
- omlish/inject/elements.py +27 -0
- omlish/inject/helpers/__init__.py +0 -0
- omlish/inject/{utils.py → helpers/constfn.py} +3 -3
- omlish/inject/{tags.py → helpers/id.py} +2 -2
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +47 -17
- omlish/inject/impl/injector.py +20 -19
- omlish/inject/impl/inspect.py +10 -1
- omlish/inject/impl/maysync.py +3 -4
- omlish/inject/impl/multis.py +3 -0
- omlish/inject/impl/sync.py +3 -4
- omlish/inject/injector.py +31 -2
- omlish/inject/inspect.py +35 -0
- omlish/inject/maysync.py +2 -4
- omlish/inject/multis.py +8 -0
- omlish/inject/overrides.py +3 -3
- omlish/inject/privates.py +6 -0
- omlish/inject/providers.py +3 -2
- omlish/inject/sync.py +5 -4
- omlish/io/buffers.py +118 -0
- omlish/io/readers.py +29 -0
- omlish/iterators/transforms.py +2 -2
- omlish/lang/__init__.py +178 -97
- omlish/lang/_asyncs.cc +186 -0
- omlish/lang/asyncs.py +17 -0
- omlish/lang/casing.py +11 -0
- omlish/lang/contextmanagers.py +28 -4
- omlish/lang/functions.py +31 -22
- omlish/lang/imports/_capture.cc +11 -9
- omlish/lang/imports/capture.py +363 -170
- omlish/lang/imports/proxy.py +455 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/lang/params.py +17 -0
- omlish/lang/recursion.py +0 -1
- omlish/lang/sequences.py +124 -0
- omlish/lite/abstract.py +54 -24
- omlish/lite/asyncs.py +2 -2
- omlish/lite/attrops.py +2 -0
- omlish/lite/contextmanagers.py +4 -4
- omlish/lite/dataclasses.py +44 -0
- omlish/lite/maybes.py +8 -0
- omlish/lite/maysync.py +1 -5
- omlish/lite/pycharm.py +1 -1
- omlish/lite/typing.py +6 -0
- omlish/logs/all.py +1 -1
- omlish/logs/utils.py +1 -1
- omlish/manifests/loading.py +2 -1
- omlish/marshal/__init__.py +33 -13
- omlish/marshal/_dataclasses.py +2774 -0
- omlish/marshal/base/configs.py +12 -0
- omlish/marshal/base/contexts.py +36 -21
- omlish/marshal/base/funcs.py +8 -11
- omlish/marshal/base/options.py +8 -0
- omlish/marshal/base/registries.py +146 -44
- omlish/marshal/base/types.py +40 -16
- omlish/marshal/composite/iterables.py +33 -20
- omlish/marshal/composite/literals.py +20 -18
- omlish/marshal/composite/mappings.py +36 -23
- omlish/marshal/composite/maybes.py +29 -19
- omlish/marshal/composite/newtypes.py +16 -16
- omlish/marshal/composite/optionals.py +14 -14
- omlish/marshal/composite/special.py +15 -15
- omlish/marshal/composite/unions/__init__.py +0 -0
- omlish/marshal/composite/unions/literals.py +93 -0
- omlish/marshal/composite/unions/primitives.py +103 -0
- omlish/marshal/factories/invalidate.py +18 -68
- omlish/marshal/factories/method.py +26 -0
- omlish/marshal/factories/moduleimport/factories.py +22 -65
- omlish/marshal/factories/multi.py +13 -25
- omlish/marshal/factories/recursive.py +42 -56
- omlish/marshal/factories/typecache.py +29 -74
- omlish/marshal/factories/typemap.py +42 -43
- omlish/marshal/objects/dataclasses.py +129 -106
- omlish/marshal/objects/marshal.py +18 -14
- omlish/marshal/objects/namedtuples.py +48 -42
- omlish/marshal/objects/unmarshal.py +19 -15
- omlish/marshal/polymorphism/marshal.py +9 -11
- omlish/marshal/polymorphism/metadata.py +16 -5
- omlish/marshal/polymorphism/standard.py +13 -1
- omlish/marshal/polymorphism/unions.py +15 -105
- omlish/marshal/polymorphism/unmarshal.py +9 -10
- omlish/marshal/singular/enums.py +14 -18
- omlish/marshal/standard.py +10 -6
- omlish/marshal/trivial/any.py +1 -1
- omlish/marshal/trivial/forbidden.py +21 -26
- omlish/metadata.py +23 -1
- omlish/os/forkhooks.py +4 -0
- omlish/os/pidfiles/pinning.py +2 -2
- omlish/reflect/types.py +1 -0
- omlish/secrets/marshal.py +1 -1
- omlish/specs/jmespath/__init__.py +12 -3
- omlish/specs/jmespath/_dataclasses.py +2893 -0
- omlish/specs/jmespath/ast.py +1 -1
- omlish/specs/jsonrpc/__init__.py +13 -0
- omlish/specs/jsonrpc/_marshal.py +32 -23
- omlish/specs/jsonrpc/conns.py +10 -7
- omlish/specs/jsonschema/_marshal.py +1 -1
- omlish/specs/jsonschema/keywords/__init__.py +7 -0
- omlish/specs/jsonschema/keywords/_dataclasses.py +1644 -0
- omlish/specs/openapi/_marshal.py +31 -22
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/queries/_marshal.py +2 -2
- omlish/sql/queries/rendering.py +1 -1
- omlish/sql/tabledefs/_marshal.py +1 -1
- omlish/subprocesses/base.py +4 -0
- omlish/subprocesses/editor.py +1 -1
- omlish/sync.py +155 -21
- omlish/term/alt.py +60 -0
- omlish/term/confirm.py +8 -8
- omlish/term/pager.py +235 -0
- omlish/term/terminfo.py +935 -0
- omlish/term/termstate.py +67 -0
- omlish/term/vt100/terminal.py +0 -3
- omlish/testing/pytest/plugins/asyncs/fixtures.py +4 -1
- omlish/testing/pytest/plugins/skips.py +2 -1
- omlish/testing/unittest/main.py +3 -3
- omlish/text/docwrap/__init__.py +3 -0
- omlish/text/docwrap/__main__.py +11 -0
- omlish/text/docwrap/api.py +83 -0
- omlish/text/docwrap/cli.py +86 -0
- omlish/text/docwrap/groups.py +84 -0
- omlish/text/docwrap/lists.py +167 -0
- omlish/text/docwrap/parts.py +146 -0
- omlish/text/docwrap/reflowing.py +106 -0
- omlish/text/docwrap/rendering.py +151 -0
- omlish/text/docwrap/utils.py +11 -0
- omlish/text/docwrap/wrapping.py +59 -0
- omlish/text/filecache.py +2 -2
- omlish/text/lorem.py +6 -0
- omlish/text/parts.py +2 -2
- omlish/text/textwrap.py +51 -0
- omlish/typedvalues/marshal.py +85 -59
- omlish/typedvalues/values.py +2 -1
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/METADATA +29 -28
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/RECORD +222 -171
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev484.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- lock?
|
|
4
|
+
"""
|
|
5
|
+
import dataclasses as dc
|
|
6
|
+
import typing as ta
|
|
7
|
+
import weakref
|
|
8
|
+
|
|
9
|
+
from .. import check
|
|
10
|
+
from .. import lang
|
|
11
|
+
from .mappings import dict_factory
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
T = ta.TypeVar('T')
|
|
15
|
+
K = ta.TypeVar('K')
|
|
16
|
+
V = ta.TypeVar('V')
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AttrRegistry(ta.Generic[K, V]):
|
|
23
|
+
"""
|
|
24
|
+
MRO-honoring class member registry. There are many ways to do this, and this one is attr name based: a class is
|
|
25
|
+
considered to have objects registered to it based on whether or not they are accessible by a non-shadowed,
|
|
26
|
+
MRO-resolved named attribute on that class.
|
|
27
|
+
|
|
28
|
+
Care must be taken when overriding registered objects from superclasses in subclasses - shadowing the attribute name
|
|
29
|
+
of the superclass member will not automatically register the new member with the same name to the registry - it must
|
|
30
|
+
be explicitly registered itself. This is a feature, allowing for selective de-registration of objects in subclasses
|
|
31
|
+
via name shadowing.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
requires_override: bool = False,
|
|
38
|
+
is_override: ta.Callable[[ta.Any], bool] | None = None,
|
|
39
|
+
forbid_duplicates: bool = False,
|
|
40
|
+
identity: bool = False,
|
|
41
|
+
weak: bool = False,
|
|
42
|
+
) -> None:
|
|
43
|
+
super().__init__()
|
|
44
|
+
|
|
45
|
+
self._requires_override = requires_override
|
|
46
|
+
self._is_override = is_override
|
|
47
|
+
self._forbid_duplicates = forbid_duplicates
|
|
48
|
+
self._identity = identity
|
|
49
|
+
self._weak = weak
|
|
50
|
+
|
|
51
|
+
self._objs: ta.MutableMapping[K, V] = dict_factory(identity=identity, weak=weak)()
|
|
52
|
+
self._invalidate_callbacks: list[ta.Callable[[], None]] = []
|
|
53
|
+
|
|
54
|
+
def add_invalidate_callback(self, callback: ta.Callable[[], None]) -> None:
|
|
55
|
+
self._invalidate_callbacks.append(callback)
|
|
56
|
+
|
|
57
|
+
@ta.overload
|
|
58
|
+
def register(self, obj: K, val: V) -> None:
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
@ta.overload
|
|
62
|
+
def register(self, val: V) -> ta.Callable[[T], T]:
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
def register(self, *args):
|
|
66
|
+
def inner(obj, val):
|
|
67
|
+
check.not_in(obj, self._objs)
|
|
68
|
+
|
|
69
|
+
self._objs[obj] = val
|
|
70
|
+
|
|
71
|
+
for iv in self._invalidate_callbacks:
|
|
72
|
+
iv()
|
|
73
|
+
|
|
74
|
+
return obj
|
|
75
|
+
|
|
76
|
+
if len(args) == 1:
|
|
77
|
+
return lambda obj: inner(obj, args[0])
|
|
78
|
+
elif len(args) == 2:
|
|
79
|
+
inner = inner(*args)
|
|
80
|
+
return None
|
|
81
|
+
else:
|
|
82
|
+
raise TypeError(args)
|
|
83
|
+
|
|
84
|
+
def _lookup(self, obj: ta.Any) -> lang.Maybe[V]:
|
|
85
|
+
if not self._identity:
|
|
86
|
+
try:
|
|
87
|
+
hash(obj)
|
|
88
|
+
except TypeError:
|
|
89
|
+
return lang.empty()
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
val = self._objs[obj]
|
|
93
|
+
except KeyError:
|
|
94
|
+
return lang.empty()
|
|
95
|
+
else:
|
|
96
|
+
return lang.just(val)
|
|
97
|
+
|
|
98
|
+
@dc.dataclass()
|
|
99
|
+
class DuplicatesForbiddenError(Exception):
|
|
100
|
+
owner_cls: type
|
|
101
|
+
instance_cls: type
|
|
102
|
+
att: str
|
|
103
|
+
ex_att: str
|
|
104
|
+
|
|
105
|
+
def collect(self, instance_cls: type, owner_cls: type | None = None) -> dict[str, tuple[K, V]]:
|
|
106
|
+
if owner_cls is None:
|
|
107
|
+
owner_cls = instance_cls
|
|
108
|
+
|
|
109
|
+
mro = instance_cls.__mro__[-2::-1]
|
|
110
|
+
try:
|
|
111
|
+
mro_pos = mro.index(owner_cls)
|
|
112
|
+
except ValueError:
|
|
113
|
+
raise TypeError(f'Owner class {owner_cls} not in mro of instance class {instance_cls}') from None
|
|
114
|
+
|
|
115
|
+
mro_dct: dict[str, list[tuple[type, ta.Any]]] = {}
|
|
116
|
+
for cur_cls in mro[:mro_pos + 1]:
|
|
117
|
+
for att, obj in cur_cls.__dict__.items():
|
|
118
|
+
if att not in mro_dct:
|
|
119
|
+
if not self._lookup(obj).present:
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
lst = mro_dct[att]
|
|
124
|
+
except KeyError:
|
|
125
|
+
lst = mro_dct[att] = []
|
|
126
|
+
lst.append((cur_cls, obj))
|
|
127
|
+
|
|
128
|
+
#
|
|
129
|
+
|
|
130
|
+
seen: ta.MutableMapping[ta.Any, str] | None = None
|
|
131
|
+
if self._forbid_duplicates:
|
|
132
|
+
seen = dict_factory(identity=self._identity)()
|
|
133
|
+
|
|
134
|
+
out: dict[str, tuple[K, V]] = {}
|
|
135
|
+
for att, lst in mro_dct.items():
|
|
136
|
+
if not lst:
|
|
137
|
+
raise RuntimeError
|
|
138
|
+
_, obj = lst[-1]
|
|
139
|
+
|
|
140
|
+
if len(lst) > 1:
|
|
141
|
+
if self._requires_override and not (self._is_override or lang.is_override)(obj):
|
|
142
|
+
raise lang.RequiresOverrideError(
|
|
143
|
+
att,
|
|
144
|
+
instance_cls,
|
|
145
|
+
lst[-1][0],
|
|
146
|
+
lst[0][0],
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
if not (mv := self._lookup(obj)).present:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
if seen is not None:
|
|
153
|
+
try:
|
|
154
|
+
ex_att = seen[obj]
|
|
155
|
+
except KeyError:
|
|
156
|
+
pass
|
|
157
|
+
else:
|
|
158
|
+
raise AttrRegistry.DuplicatesForbiddenError(owner_cls, instance_cls, att, ex_att) # noqa
|
|
159
|
+
seen[obj] = att
|
|
160
|
+
|
|
161
|
+
out[att] = (obj, mv.must())
|
|
162
|
+
|
|
163
|
+
return out
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class AttrRegistryCache(ta.Generic[K, V, T]):
|
|
170
|
+
def __init__(
|
|
171
|
+
self,
|
|
172
|
+
registry: AttrRegistry[K, V],
|
|
173
|
+
prepare: ta.Callable[[type, dict[str, tuple[K, V]]], T],
|
|
174
|
+
) -> None:
|
|
175
|
+
super().__init__()
|
|
176
|
+
|
|
177
|
+
self._registry = registry
|
|
178
|
+
self._prepare = prepare
|
|
179
|
+
|
|
180
|
+
self._cache: dict[ta.Any, T] = {}
|
|
181
|
+
|
|
182
|
+
def cache_remove(k, self_ref=weakref.ref(self)):
|
|
183
|
+
if (ref_self := self_ref()) is not None:
|
|
184
|
+
cache = ref_self._cache # noqa
|
|
185
|
+
try:
|
|
186
|
+
del cache[k]
|
|
187
|
+
except KeyError:
|
|
188
|
+
pass
|
|
189
|
+
|
|
190
|
+
self._cache_remove = cache_remove
|
|
191
|
+
|
|
192
|
+
registry.add_invalidate_callback(self._cache.clear)
|
|
193
|
+
|
|
194
|
+
def get(self, instance_cls: type) -> T:
|
|
195
|
+
cls_ref = weakref.ref(instance_cls)
|
|
196
|
+
try:
|
|
197
|
+
return self._cache[cls_ref]
|
|
198
|
+
except KeyError:
|
|
199
|
+
pass
|
|
200
|
+
del cls_ref
|
|
201
|
+
|
|
202
|
+
collected = self._registry.collect(instance_cls)
|
|
203
|
+
out = self._prepare(instance_cls, collected)
|
|
204
|
+
self._cache[weakref.ref(instance_cls, self._cache_remove)] = out
|
|
205
|
+
return out
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class SimpleAttrRegistryCache(AttrRegistryCache[K, V, dict[str, tuple[K, V]]], ta.Generic[K, V]):
|
|
209
|
+
def __init__(self, registry: AttrRegistry[K, V]) -> None:
|
|
210
|
+
super().__init__(registry, lambda _, dct: dct)
|
omlish/collections/cache/impl.py
CHANGED
omlish/collections/identity.py
CHANGED
omlish/collections/mappings.py
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
import weakref
|
|
3
3
|
|
|
4
|
+
from .. import lang
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
from . import identity as _identity
|
|
9
|
+
|
|
4
10
|
|
|
5
11
|
T = ta.TypeVar('T')
|
|
6
12
|
K = ta.TypeVar('K')
|
|
@@ -63,6 +69,9 @@ class TypeMap(ta.Generic[T]):
|
|
|
63
69
|
def __iter__(self) -> ta.Iterator[T]:
|
|
64
70
|
return iter(self._items)
|
|
65
71
|
|
|
72
|
+
def __contains__(self, ty: type[T]) -> bool:
|
|
73
|
+
return ty in self._items
|
|
74
|
+
|
|
66
75
|
def get(self, ty: type[T]) -> T | None:
|
|
67
76
|
return self._dct.get(ty)
|
|
68
77
|
|
|
@@ -138,3 +147,22 @@ class MissingDict(dict[K, V]):
|
|
|
138
147
|
def __missing__(self, key):
|
|
139
148
|
v = self[key] = self._missing_fn(key)
|
|
140
149
|
return v
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
##
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def dict_factory[K, V](
|
|
156
|
+
*,
|
|
157
|
+
identity: bool = False,
|
|
158
|
+
weak: bool = False,
|
|
159
|
+
) -> ta.Callable[..., ta.MutableMapping[K, V]]:
|
|
160
|
+
if identity:
|
|
161
|
+
if weak:
|
|
162
|
+
return _identity.IdentityWeakKeyDictionary
|
|
163
|
+
else:
|
|
164
|
+
return _identity.IdentityKeyDict
|
|
165
|
+
elif weak:
|
|
166
|
+
return weakref.WeakKeyDictionary
|
|
167
|
+
else:
|
|
168
|
+
return dict
|
omlish/collections/trie.py
CHANGED
|
@@ -37,12 +37,16 @@ class Trie(ta.MutableMapping[ta.Sequence[K], V], ta.Generic[K, V]):
|
|
|
37
37
|
def children(self) -> ta.Mapping[K2, 'Trie.Node[K2, V2]']:
|
|
38
38
|
return self._children
|
|
39
39
|
|
|
40
|
-
def __init__(self) -> None:
|
|
40
|
+
def __init__(self, items: ta.Iterable[tuple[ta.Iterable[K], V]] | None = None) -> None:
|
|
41
41
|
super().__init__()
|
|
42
42
|
|
|
43
43
|
self._len = 0
|
|
44
44
|
self._root: Trie.Node[K, V] = Trie.Node()
|
|
45
45
|
|
|
46
|
+
if items is not None:
|
|
47
|
+
for k, v in items:
|
|
48
|
+
self[k] = v
|
|
49
|
+
|
|
46
50
|
@property
|
|
47
51
|
def root(self) -> Node[K, V]:
|
|
48
52
|
return self._root
|
omlish/collections/utils.py
CHANGED
|
@@ -7,7 +7,9 @@ from .identity import IdentitySet
|
|
|
7
7
|
|
|
8
8
|
T = ta.TypeVar('T')
|
|
9
9
|
K = ta.TypeVar('K')
|
|
10
|
+
K2 = ta.TypeVar('K2')
|
|
10
11
|
V = ta.TypeVar('V')
|
|
12
|
+
V2 = ta.TypeVar('V2')
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
##
|
|
@@ -135,6 +137,81 @@ def make_map_by(
|
|
|
135
137
|
##
|
|
136
138
|
|
|
137
139
|
|
|
140
|
+
@ta.overload
|
|
141
|
+
def map_keys(
|
|
142
|
+
fn: ta.Callable[[K], K2],
|
|
143
|
+
dct: ta.Mapping[K, V] | ta.Iterable[tuple[K, V]],
|
|
144
|
+
*,
|
|
145
|
+
identity: ta.Literal[False] = False,
|
|
146
|
+
strict: bool = False,
|
|
147
|
+
) -> dict[K2, V]:
|
|
148
|
+
return {}
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@ta.overload
|
|
152
|
+
def map_keys(
|
|
153
|
+
fn: ta.Callable[[K], K2],
|
|
154
|
+
dct: ta.Mapping[K, V] | ta.Iterable[tuple[K, V]],
|
|
155
|
+
*,
|
|
156
|
+
identity: bool = False,
|
|
157
|
+
strict: bool = False,
|
|
158
|
+
) -> ta.MutableMapping[K2, V]:
|
|
159
|
+
return {}
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def map_keys(
|
|
163
|
+
fn,
|
|
164
|
+
dct,
|
|
165
|
+
*,
|
|
166
|
+
identity=False,
|
|
167
|
+
strict=False,
|
|
168
|
+
):
|
|
169
|
+
return make_map(
|
|
170
|
+
((fn(k), v) for k, v in (dct.items() if isinstance(dct, ta.Mapping) else dct)),
|
|
171
|
+
identity=identity,
|
|
172
|
+
strict=strict,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@ta.overload
|
|
177
|
+
def map_values(
|
|
178
|
+
fn: ta.Callable[[V], V2],
|
|
179
|
+
dct: ta.Mapping[K, V] | ta.Iterable[tuple[K, V]],
|
|
180
|
+
*,
|
|
181
|
+
identity: ta.Literal[False] = False,
|
|
182
|
+
strict: bool = False,
|
|
183
|
+
) -> dict[K, V2]:
|
|
184
|
+
return {}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@ta.overload
|
|
188
|
+
def map_values(
|
|
189
|
+
fn: ta.Callable[[V], V2],
|
|
190
|
+
dct: ta.Mapping[K, V] | ta.Iterable[tuple[K, V]],
|
|
191
|
+
*,
|
|
192
|
+
identity: bool = False,
|
|
193
|
+
strict: bool = False,
|
|
194
|
+
) -> ta.MutableMapping[K2, V]:
|
|
195
|
+
return {}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def map_values(
|
|
199
|
+
fn,
|
|
200
|
+
dct,
|
|
201
|
+
*,
|
|
202
|
+
identity=False,
|
|
203
|
+
strict=False,
|
|
204
|
+
):
|
|
205
|
+
return make_map(
|
|
206
|
+
((k, fn(v)) for k, v in (dct.items() if isinstance(dct, ta.Mapping) else dct)),
|
|
207
|
+
identity=identity,
|
|
208
|
+
strict=strict,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
|
|
214
|
+
|
|
138
215
|
@ta.overload
|
|
139
216
|
def multi_map(
|
|
140
217
|
kvs: ta.Iterable[tuple[K, V]],
|
omlish/concurrent/all.py
CHANGED
omlish/concurrent/futures.py
CHANGED
|
@@ -48,6 +48,13 @@ def wait_futures(
|
|
|
48
48
|
raise_exceptions: bool = False,
|
|
49
49
|
cancel_on_exception: bool = False,
|
|
50
50
|
) -> bool:
|
|
51
|
+
# TODO:
|
|
52
|
+
# - raise_exceptions 'at_end', ExceptionGroup
|
|
53
|
+
# - more responsive than tick_interval_s - semaphore, add callbacks to each fut to decrement, sleep with a timed
|
|
54
|
+
# wait on sem
|
|
55
|
+
# - cancel_on_timeout
|
|
56
|
+
# - obviate wait_all_futures_or_raise
|
|
57
|
+
|
|
51
58
|
start = time.time()
|
|
52
59
|
|
|
53
60
|
not_done = set(futures)
|
|
@@ -72,6 +79,24 @@ def wait_futures(
|
|
|
72
79
|
return False
|
|
73
80
|
|
|
74
81
|
|
|
82
|
+
def wait_all_futures_or_raise(futures: ta.Sequence[cf.Future]) -> None:
|
|
83
|
+
done, not_done = cf.wait(futures, return_when=cf.ALL_COMPLETED)
|
|
84
|
+
if not_done:
|
|
85
|
+
raise RuntimeError(f'Not all futures finished: {not_done}')
|
|
86
|
+
|
|
87
|
+
excs: list[Exception] = []
|
|
88
|
+
for f in done:
|
|
89
|
+
try:
|
|
90
|
+
f.result()
|
|
91
|
+
except Exception as e: # noqa
|
|
92
|
+
excs.append(e)
|
|
93
|
+
|
|
94
|
+
if len(excs) == 1:
|
|
95
|
+
raise excs[0]
|
|
96
|
+
elif excs:
|
|
97
|
+
raise ExceptionGroup('One or more futures failed', excs)
|
|
98
|
+
|
|
99
|
+
|
|
75
100
|
def wait_dependent_futures(
|
|
76
101
|
executor: cf.Executor,
|
|
77
102
|
dependency_sets_by_fn: ta.Mapping[ta.Callable, ta.AbstractSet[ta.Callable]],
|
omlish/concurrent/threadlets.py
CHANGED
|
@@ -75,7 +75,7 @@ class GreenletThreadlet(Threadlet):
|
|
|
75
75
|
|
|
76
76
|
@property
|
|
77
77
|
def parent(self) -> ta.Optional['GreenletThreadlet']:
|
|
78
|
-
return GreenletThreadlet(self.g.parent)
|
|
78
|
+
return GreenletThreadlet(p) if (p := self.g.parent) is not None else None
|
|
79
79
|
|
|
80
80
|
@property
|
|
81
81
|
def dead(self) -> bool:
|
omlish/daemons/reparent.py
CHANGED
|
@@ -10,13 +10,12 @@ def reparent_process(
|
|
|
10
10
|
no_close_stdio: bool = False,
|
|
11
11
|
) -> None:
|
|
12
12
|
if (pid := os.fork()): # noqa
|
|
13
|
-
|
|
14
|
-
raise RuntimeError('Unreachable') # noqa
|
|
13
|
+
raise SystemExit(0)
|
|
15
14
|
|
|
16
15
|
os.setsid()
|
|
17
16
|
|
|
18
17
|
if (pid := os.fork()): # noqa
|
|
19
|
-
|
|
18
|
+
raise SystemExit(0)
|
|
20
19
|
|
|
21
20
|
if not no_close_stdio:
|
|
22
21
|
rn_fd = os.open('/dev/null', os.O_RDONLY)
|
omlish/daemons/spawning.py
CHANGED
|
@@ -2,7 +2,6 @@ import abc
|
|
|
2
2
|
import enum
|
|
3
3
|
import functools
|
|
4
4
|
import os
|
|
5
|
-
import sys
|
|
6
5
|
import threading
|
|
7
6
|
import typing as ta
|
|
8
7
|
|
|
@@ -166,9 +165,9 @@ class ForkSpawner(Spawner, dc.Frozen):
|
|
|
166
165
|
try:
|
|
167
166
|
spawn.fn()
|
|
168
167
|
except BaseException: # noqa
|
|
169
|
-
|
|
168
|
+
raise SystemExit(1) from None
|
|
170
169
|
else:
|
|
171
|
-
|
|
170
|
+
raise SystemExit(0)
|
|
172
171
|
|
|
173
172
|
raise RuntimeError('Unreachable') # noqa
|
|
174
173
|
|
omlish/dataclasses/__init__.py
CHANGED
|
@@ -10,6 +10,7 @@ from ..... import lang
|
|
|
10
10
|
from ...._internals import STD_FIELDS_ATTR
|
|
11
11
|
from ...._internals import STD_PARAMS_ATTR
|
|
12
12
|
from ....specs import ClassSpec
|
|
13
|
+
from ....specs import ReprFn
|
|
13
14
|
from ...processing.driving import drive_cls_processing
|
|
14
15
|
from ...utils import class_decorator
|
|
15
16
|
from ..fields.building import build_cls_std_fields
|
|
@@ -53,6 +54,7 @@ def dataclass(
|
|
|
53
54
|
|
|
54
55
|
repr_id: bool | None = None,
|
|
55
56
|
terse_repr: bool | None = None,
|
|
57
|
+
default_repr_fn: ReprFn | None = None,
|
|
56
58
|
|
|
57
59
|
allow_redundant_decorator: bool | None = None,
|
|
58
60
|
|
|
@@ -158,6 +160,7 @@ def dataclass(
|
|
|
158
160
|
|
|
159
161
|
repr_id=repr_id,
|
|
160
162
|
terse_repr=terse_repr,
|
|
163
|
+
default_repr_fn=default_repr_fn,
|
|
161
164
|
|
|
162
165
|
allow_redundant_decorator=allow_redundant_decorator,
|
|
163
166
|
),
|
|
@@ -4,6 +4,7 @@ import sys
|
|
|
4
4
|
import types
|
|
5
5
|
import typing as ta
|
|
6
6
|
|
|
7
|
+
from ....specs import ReprFn
|
|
7
8
|
from .decorator import dataclass
|
|
8
9
|
|
|
9
10
|
|
|
@@ -53,6 +54,7 @@ def make_dataclass( # noqa
|
|
|
53
54
|
|
|
54
55
|
repr_id: bool | None = None,
|
|
55
56
|
terse_repr: bool | None = None,
|
|
57
|
+
default_repr_fn: ReprFn | None = None,
|
|
56
58
|
|
|
57
59
|
allow_redundant_decorator: bool | None = None,
|
|
58
60
|
|
|
@@ -174,6 +176,7 @@ def make_dataclass( # noqa
|
|
|
174
176
|
|
|
175
177
|
repr_id=repr_id,
|
|
176
178
|
terse_repr=terse_repr,
|
|
179
|
+
default_repr_fn=default_repr_fn,
|
|
177
180
|
|
|
178
181
|
allow_redundant_decorator=allow_redundant_decorator,
|
|
179
182
|
)
|
|
@@ -32,6 +32,7 @@ class ReprPlan(Plan):
|
|
|
32
32
|
|
|
33
33
|
id: bool = False
|
|
34
34
|
terse: bool = False
|
|
35
|
+
default_fn: OpRef[ReprFn] | None = None
|
|
35
36
|
|
|
36
37
|
|
|
37
38
|
@register_generator_type(ReprPlan)
|
|
@@ -66,11 +67,17 @@ class ReprGenerator(Generator[ReprPlan]):
|
|
|
66
67
|
fn=fnr,
|
|
67
68
|
))
|
|
68
69
|
|
|
70
|
+
drf: OpRef | None = None
|
|
71
|
+
if ctx.cs.default_repr_fn is not None:
|
|
72
|
+
drf = OpRef(f'repr.default_fn')
|
|
73
|
+
orm[drf] = ctx.cs.default_repr_fn
|
|
74
|
+
|
|
69
75
|
return PlanResult(
|
|
70
76
|
ReprPlan(
|
|
71
77
|
fields=tuple(rfs),
|
|
72
78
|
id=ctx.cs.repr_id,
|
|
73
79
|
terse=ctx.cs.terse_repr,
|
|
80
|
+
default_fn=drf,
|
|
74
81
|
),
|
|
75
82
|
orm,
|
|
76
83
|
)
|
|
@@ -85,10 +92,16 @@ class ReprGenerator(Generator[ReprPlan]):
|
|
|
85
92
|
if not (pl.terse and not f.kw_only):
|
|
86
93
|
pfx = f'{f.name}='
|
|
87
94
|
|
|
95
|
+
fn: OpRef[ReprFn] | None = None
|
|
88
96
|
if f.fn is not None:
|
|
89
|
-
|
|
97
|
+
fn = f.fn
|
|
98
|
+
elif pl.default_fn is not None:
|
|
99
|
+
fn = pl.default_fn
|
|
100
|
+
|
|
101
|
+
if fn is not None:
|
|
102
|
+
ors.add(fn)
|
|
90
103
|
part_lines.extend([
|
|
91
|
-
f' if (s := {
|
|
104
|
+
f' if (s := {fn.ident()}(self.{f.name})) is not None:',
|
|
92
105
|
f' parts.append(f"{pfx}{{s}}")',
|
|
93
106
|
])
|
|
94
107
|
else:
|