omlish 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev493__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/README.md +199 -0
- omlish/__about__.py +21 -16
- 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/plans.py +2 -17
- omlish/dataclasses/impl/generation/processor.py +106 -25
- omlish/dataclasses/impl/processing/base.py +8 -0
- omlish/dataclasses/impl/processing/driving.py +19 -7
- omlish/dataclasses/specs.py +1 -0
- omlish/dataclasses/tools/modifiers.py +5 -0
- omlish/diag/_pycharm/runhack.py +1 -1
- 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 +57 -29
- omlish/inject/_dataclasses.py +5148 -0
- omlish/inject/binder.py +11 -52
- omlish/inject/eagers.py +2 -0
- 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/late.py +76 -0
- omlish/inject/{managed.py → helpers/managed.py} +10 -10
- omlish/inject/helpers/multis.py +143 -0
- omlish/inject/helpers/wrappers.py +54 -0
- omlish/inject/impl/elements.py +54 -21
- omlish/inject/impl/injector.py +29 -25
- 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 +180 -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/lifecycles/README.md +30 -0
- omlish/lifecycles/__init__.py +87 -13
- omlish/lifecycles/_dataclasses.py +1388 -0
- omlish/lifecycles/base.py +178 -64
- omlish/lifecycles/contextmanagers.py +113 -4
- omlish/lifecycles/controller.py +150 -87
- omlish/lifecycles/injection.py +143 -0
- omlish/lifecycles/listeners.py +56 -0
- omlish/lifecycles/managed.py +142 -0
- omlish/lifecycles/manager.py +218 -93
- omlish/lifecycles/states.py +2 -0
- omlish/lifecycles/transitions.py +3 -0
- omlish/lifecycles/unwrap.py +57 -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 +24 -0
- omlish/logs/_amalg.py +1 -1
- omlish/logs/all.py +25 -11
- omlish/logs/asyncs.py +73 -0
- omlish/logs/base.py +101 -12
- omlish/logs/contexts.py +4 -1
- omlish/logs/lists.py +125 -0
- omlish/logs/modules.py +19 -1
- omlish/logs/std/loggers.py +6 -1
- omlish/logs/std/noisy.py +11 -9
- omlish/logs/{standard.py → std/standard.py} +3 -4
- omlish/logs/utils.py +17 -2
- 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/__init__.py +43 -26
- omlish/reflect/ops.py +10 -1
- 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/__init__.py +24 -5
- omlish/sql/{tabledefs/alchemy.py → alchemy/tabledefs.py} +2 -2
- omlish/sql/api/dbapi.py +1 -1
- omlish/sql/dbapi/__init__.py +15 -0
- omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
- omlish/sql/queries/__init__.py +3 -0
- 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/asyncs/plugin.py +2 -0
- 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 +91 -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.dev493.dist-info}/METADATA +36 -32
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/RECORD +260 -199
- omlish/dataclasses/impl/generation/mangling.py +0 -18
- omlish/funcs/match.py +0 -227
- omlish/lifecycles/abstract.py +0 -86
- omlish/marshal/factories/match.py +0 -34
- omlish/marshal/factories/simple.py +0 -28
- /omlish/inject/{impl → helpers}/proxy.py +0 -0
- /omlish/inject/impl/{providers2.py → providersmap.py} +0 -0
- /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev447.dist-info → omlish-0.0.0.dev493.dist-info}/top_level.txt +0 -0
omlish/bootstrap/diag.py
CHANGED
|
@@ -13,23 +13,14 @@ from .base import ContextBootstrap
|
|
|
13
13
|
from .base import SimpleBootstrap
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
with lang.auto_proxy_import(globals()):
|
|
17
17
|
import cProfile # noqa
|
|
18
18
|
import pstats
|
|
19
19
|
|
|
20
|
-
from ..diag import debug as
|
|
21
|
-
from ..diag import pycharm as
|
|
22
|
-
from ..diag import replserver
|
|
23
|
-
from ..diag import threads as
|
|
24
|
-
|
|
25
|
-
else:
|
|
26
|
-
cProfile = lang.proxy_import('cProfile') # noqa
|
|
27
|
-
pstats = lang.proxy_import('pstats')
|
|
28
|
-
|
|
29
|
-
ddbg = lang.proxy_import('..diag.debug', __package__)
|
|
30
|
-
diagpc = lang.proxy_import('..diag.pycharm', __package__)
|
|
31
|
-
diagrs = lang.proxy_import('..diag.replserver', __package__)
|
|
32
|
-
diagt = lang.proxy_import('..diag.threads', __package__)
|
|
20
|
+
from ..diag import debug as d_debug
|
|
21
|
+
from ..diag import pycharm as d_pycharm
|
|
22
|
+
from ..diag import replserver
|
|
23
|
+
from ..diag import threads as d_threads
|
|
33
24
|
|
|
34
25
|
|
|
35
26
|
##
|
|
@@ -110,7 +101,7 @@ class ThreadDumpBootstrap(ContextBootstrap['ThreadDumpBootstrap.Config']):
|
|
|
110
101
|
@contextlib.contextmanager
|
|
111
102
|
def enter(self) -> ta.Iterator[None]:
|
|
112
103
|
if self._config.interval_s:
|
|
113
|
-
tdt =
|
|
104
|
+
tdt = d_threads.create_thread_dump_thread(
|
|
114
105
|
interval_s=self._config.interval_s,
|
|
115
106
|
start=True,
|
|
116
107
|
nodaemon=self._config.nodaemon,
|
|
@@ -119,7 +110,7 @@ class ThreadDumpBootstrap(ContextBootstrap['ThreadDumpBootstrap.Config']):
|
|
|
119
110
|
tdt = None
|
|
120
111
|
|
|
121
112
|
if self._config.on_sigquit:
|
|
122
|
-
dump_threads_str =
|
|
113
|
+
dump_threads_str = d_threads.dump_threads_str
|
|
123
114
|
|
|
124
115
|
def handler(signum, frame):
|
|
125
116
|
print(dump_threads_str(), file=sys.stderr)
|
|
@@ -153,7 +144,7 @@ class TimebombBootstrap(ContextBootstrap['TimebombBootstrap.Config']):
|
|
|
153
144
|
yield
|
|
154
145
|
return
|
|
155
146
|
|
|
156
|
-
tbt =
|
|
147
|
+
tbt = d_threads.create_timebomb_thread(
|
|
157
148
|
self._config.delay_s,
|
|
158
149
|
start=True,
|
|
159
150
|
)
|
|
@@ -173,8 +164,8 @@ class PycharmBootstrap(SimpleBootstrap['PycharmBootstrap.Config']):
|
|
|
173
164
|
|
|
174
165
|
def run(self) -> None:
|
|
175
166
|
if self._config.debug is not None:
|
|
176
|
-
prd =
|
|
177
|
-
|
|
167
|
+
prd = d_pycharm.PycharmRemoteDebugger.parse(self._config.debug)
|
|
168
|
+
d_pycharm.pycharm_remote_debugger_attach(prd)
|
|
178
169
|
|
|
179
170
|
|
|
180
171
|
##
|
|
@@ -190,7 +181,7 @@ class ReplServerBootstrap(ContextBootstrap['ReplServerBootstrap.Config']):
|
|
|
190
181
|
if self._config.path is None:
|
|
191
182
|
return
|
|
192
183
|
|
|
193
|
-
with
|
|
184
|
+
with replserver.ReplServer(replserver.ReplServer.Config(
|
|
194
185
|
path=self._config.path,
|
|
195
186
|
)) as rs:
|
|
196
187
|
thread = threading.Thread(target=rs.run, name='replserver')
|
|
@@ -212,5 +203,5 @@ class DebugBootstrap(ContextBootstrap['DebugBootstrap.Config']):
|
|
|
212
203
|
if not self._config.enable:
|
|
213
204
|
return
|
|
214
205
|
|
|
215
|
-
with
|
|
206
|
+
with d_debug.debugging_on_exception():
|
|
216
207
|
yield
|
omlish/bootstrap/main.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP045
|
|
1
2
|
"""
|
|
2
3
|
TODO:
|
|
3
4
|
- -x / --exec - os.exec entrypoint
|
|
4
5
|
- refuse to install non-exec-relevant Bootstraps when chosen
|
|
5
6
|
"""
|
|
6
|
-
# ruff: noqa: UP006 UP007 UP045
|
|
7
7
|
import argparse
|
|
8
8
|
import dataclasses as dc
|
|
9
9
|
import io
|
|
@@ -19,12 +19,9 @@ from .harness import BOOTSTRAP_TYPES_BY_NAME
|
|
|
19
19
|
from .harness import bootstrap
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
with lang.auto_proxy_import(globals()):
|
|
23
23
|
import runpy
|
|
24
24
|
|
|
25
|
-
else:
|
|
26
|
-
runpy = lang.proxy_import('runpy')
|
|
27
|
-
|
|
28
25
|
|
|
29
26
|
##
|
|
30
27
|
|
omlish/bootstrap/sys.py
CHANGED
|
@@ -2,14 +2,10 @@
|
|
|
2
2
|
import contextlib
|
|
3
3
|
import dataclasses as dc
|
|
4
4
|
import enum
|
|
5
|
-
import faulthandler
|
|
6
5
|
import gc
|
|
7
6
|
import importlib
|
|
8
7
|
import logging
|
|
9
8
|
import os
|
|
10
|
-
import pwd
|
|
11
|
-
import resource
|
|
12
|
-
import signal
|
|
13
9
|
import sys
|
|
14
10
|
import typing as ta
|
|
15
11
|
|
|
@@ -19,18 +15,17 @@ from .base import ContextBootstrap
|
|
|
19
15
|
from .base import SimpleBootstrap
|
|
20
16
|
|
|
21
17
|
|
|
22
|
-
|
|
18
|
+
with lang.auto_proxy_import(globals()):
|
|
19
|
+
import faulthandler
|
|
20
|
+
import pwd
|
|
21
|
+
import resource
|
|
22
|
+
import signal
|
|
23
|
+
|
|
23
24
|
from .. import libc
|
|
24
25
|
from ..formats import dotenv
|
|
25
26
|
from ..logs import all as logs
|
|
26
27
|
from ..os.pidfiles import pidfile
|
|
27
28
|
|
|
28
|
-
else:
|
|
29
|
-
libc = lang.proxy_import('..libc', __package__)
|
|
30
|
-
logs = lang.proxy_import('..logs', __package__)
|
|
31
|
-
dotenv = lang.proxy_import('..formats.dotenv', __package__)
|
|
32
|
-
pidfile = lang.proxy_import('..os.pidfiles.pidfile', __package__)
|
|
33
|
-
|
|
34
29
|
|
|
35
30
|
##
|
|
36
31
|
|
|
@@ -184,14 +179,16 @@ class FaulthandlerBootstrap(ContextBootstrap['FaulthandlerBootstrap.Config']):
|
|
|
184
179
|
##
|
|
185
180
|
|
|
186
181
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
182
|
+
@lang.cached_function
|
|
183
|
+
def signals_by_name() -> ta.Mapping[str, int]:
|
|
184
|
+
return {
|
|
185
|
+
a[len('SIG'):]: v # noqa
|
|
186
|
+
for a in dir(signal)
|
|
187
|
+
if a.startswith('SIG')
|
|
188
|
+
and not a.startswith('SIG_')
|
|
189
|
+
and a == a.upper()
|
|
190
|
+
and isinstance((v := getattr(signal, a)), int)
|
|
191
|
+
}
|
|
195
192
|
|
|
196
193
|
|
|
197
194
|
class PrctlBootstrap(SimpleBootstrap['PrctlBootstrap.Config']):
|
|
@@ -208,20 +205,22 @@ class PrctlBootstrap(SimpleBootstrap['PrctlBootstrap.Config']):
|
|
|
208
205
|
if isinstance(self._config.deathsig, int):
|
|
209
206
|
sig = self._config.deathsig
|
|
210
207
|
else:
|
|
211
|
-
sig =
|
|
208
|
+
sig = signals_by_name()[self._config.deathsig.upper()]
|
|
212
209
|
libc.prctl(libc.PR_SET_PDEATHSIG, sig, 0, 0, 0, 0)
|
|
213
210
|
|
|
214
211
|
|
|
215
212
|
##
|
|
216
213
|
|
|
217
214
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
215
|
+
@lang.cached_function
|
|
216
|
+
def rlimits_by_name() -> ta.Mapping[str, int]:
|
|
217
|
+
return {
|
|
218
|
+
a[len('RLIMIT_'):]: v # noqa
|
|
219
|
+
for a in dir(resource)
|
|
220
|
+
if a.startswith('RLIMIT_')
|
|
221
|
+
and a == a.upper()
|
|
222
|
+
and isinstance((v := getattr(resource, a)), int)
|
|
223
|
+
}
|
|
225
224
|
|
|
226
225
|
|
|
227
226
|
class RlimitBootstrap(ContextBootstrap['RlimitBootstrap.Config']):
|
|
@@ -240,7 +239,7 @@ class RlimitBootstrap(ContextBootstrap['RlimitBootstrap.Config']):
|
|
|
240
239
|
|
|
241
240
|
prev = {}
|
|
242
241
|
for k, (s, h) in self._config.limits.items():
|
|
243
|
-
i =
|
|
242
|
+
i = rlimits_by_name()[k.upper()]
|
|
244
243
|
prev[i] = resource.getrlimit(i)
|
|
245
244
|
resource.setrlimit(i, (or_infin(s), or_infin(h)))
|
|
246
245
|
|
omlish/cexts/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#pragma once
|
omlish/collections/__init__.py
CHANGED
|
@@ -6,12 +6,21 @@ from .. import lang as _lang
|
|
|
6
6
|
with _lang.auto_proxy_init(globals()):
|
|
7
7
|
##
|
|
8
8
|
|
|
9
|
+
from .attrregistry import ( # noqa
|
|
10
|
+
AttrRegistry,
|
|
11
|
+
|
|
12
|
+
AttrRegistryCache,
|
|
13
|
+
SimpleAttrRegistryCache,
|
|
14
|
+
)
|
|
15
|
+
|
|
9
16
|
from .bimap import ( # noqa
|
|
10
17
|
BiMap,
|
|
11
18
|
|
|
12
19
|
make_bi_map,
|
|
13
20
|
)
|
|
14
21
|
|
|
22
|
+
from . import cache # noqa
|
|
23
|
+
|
|
15
24
|
from .coerce import ( # noqa
|
|
16
25
|
abs_set,
|
|
17
26
|
abs_set_of,
|
|
@@ -64,9 +73,10 @@ with _lang.auto_proxy_init(globals()):
|
|
|
64
73
|
from . import kv # noqa
|
|
65
74
|
|
|
66
75
|
from .mappings import ( # noqa
|
|
76
|
+
DynamicTypeMap,
|
|
67
77
|
MissingDict,
|
|
68
78
|
TypeMap,
|
|
69
|
-
|
|
79
|
+
dict_factory,
|
|
70
80
|
guarded_map_update,
|
|
71
81
|
multikey_dict,
|
|
72
82
|
)
|
|
@@ -145,6 +155,8 @@ with _lang.auto_proxy_init(globals()):
|
|
|
145
155
|
key_cmp,
|
|
146
156
|
make_map,
|
|
147
157
|
make_map_by,
|
|
158
|
+
map_keys,
|
|
159
|
+
map_values,
|
|
148
160
|
multi_map,
|
|
149
161
|
multi_map_by,
|
|
150
162
|
partition,
|
|
@@ -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]],
|