omlish 0.0.0.dev4__tar.gz → 0.0.0.dev6__tar.gz
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-0.0.0.dev4/omlish.egg-info → omlish-0.0.0.dev6}/PKG-INFO +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/__about__.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/__init__.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/asyncs/__init__.py +10 -4
- omlish-0.0.0.dev6/omlish/asyncs/anyio.py +216 -0
- omlish-0.0.0.dev6/omlish/asyncs/asyncio.py +42 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/asyncs/asyncs.py +9 -6
- omlish-0.0.0.dev6/omlish/asyncs/bridge.py +316 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/asyncs/flavors.py +27 -1
- omlish-0.0.0.dev6/omlish/asyncs/trio_asyncio.py +51 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/c3.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/cached.py +1 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/__init__.py +5 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/cache/impl.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/identity.py +7 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/utils.py +38 -6
- omlish-0.0.0.dev6/omlish/configs/__init__.py +5 -0
- omlish-0.0.0.dev6/omlish/configs/classes.py +53 -0
- omlish-0.0.0.dev6/omlish/configs/strings.py +94 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/__init__.py +9 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/api.py +1 -1
- omlish-0.0.0.dev6/omlish/dataclasses/impl/copy.py +30 -0
- omlish-0.0.0.dev6/omlish/dataclasses/impl/exceptions.py +8 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/fields.py +25 -25
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/init.py +5 -3
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/main.py +3 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/metaclass.py +6 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/reflect.py +15 -2
- omlish-0.0.0.dev6/omlish/dataclasses/utils.py +44 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/defs.py +1 -1
- omlish-0.0.0.dev6/omlish/diag/__init__.py +4 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/procfs.py +31 -3
- omlish-0.0.0.dev6/omlish/diag/procstats.py +32 -0
- {omlish-0.0.0.dev4/omlish/testing → omlish-0.0.0.dev6/omlish/diag}/pydevd.py +35 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/replserver/console.py +3 -3
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/replserver/server.py +6 -5
- omlish-0.0.0.dev6/omlish/diag/threads.py +86 -0
- omlish-0.0.0.dev6/omlish/dispatch/_dispatch2.py +65 -0
- omlish-0.0.0.dev6/omlish/dispatch/_dispatch3.py +104 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/docker.py +20 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/fnpairs.py +37 -18
- omlish-0.0.0.dev6/omlish/graphs/dags.py +113 -0
- omlish-0.0.0.dev6/omlish/graphs/domination.py +268 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/graphs/trees.py +2 -2
- omlish-0.0.0.dev6/omlish/http/__init__.py +25 -0
- omlish-0.0.0.dev6/omlish/http/asgi.py +132 -0
- omlish-0.0.0.dev6/omlish/http/collections.py +15 -0
- omlish-0.0.0.dev6/omlish/http/consts.py +62 -0
- omlish-0.0.0.dev6/omlish/http/cookies.py +194 -0
- omlish-0.0.0.dev6/omlish/http/dates.py +70 -0
- omlish-0.0.0.dev6/omlish/http/encodings.py +6 -0
- omlish-0.0.0.dev6/omlish/http/json.py +273 -0
- omlish-0.0.0.dev6/omlish/http/sessions.py +204 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/__init__.py +51 -17
- omlish-0.0.0.dev6/omlish/inject/binder.py +192 -0
- omlish-0.0.0.dev6/omlish/inject/bindings.py +16 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/eagers.py +2 -8
- omlish-0.0.0.dev6/omlish/inject/elements.py +64 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/exceptions.py +3 -3
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/elements.py +65 -31
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/injector.py +20 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/inspect.py +33 -5
- omlish-0.0.0.dev6/omlish/inject/impl/multis.py +74 -0
- omlish-0.0.0.dev6/omlish/inject/impl/origins.py +75 -0
- omlish-0.0.0.dev4/omlish/inject/impl/private.py → omlish-0.0.0.dev6/omlish/inject/impl/privates.py +2 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/providers.py +19 -39
- {omlish-0.0.0.dev4/omlish/inject → omlish-0.0.0.dev6/omlish/inject/impl}/proxy.py +2 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/scopes.py +7 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/injector.py +9 -4
- omlish-0.0.0.dev6/omlish/inject/inspect.py +32 -0
- omlish-0.0.0.dev6/omlish/inject/keys.py +31 -0
- omlish-0.0.0.dev6/omlish/inject/listeners.py +26 -0
- omlish-0.0.0.dev6/omlish/inject/managed.py +90 -0
- omlish-0.0.0.dev6/omlish/inject/multis.py +120 -0
- omlish-0.0.0.dev6/omlish/inject/origins.py +27 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/overrides.py +5 -4
- omlish-0.0.0.dev4/omlish/inject/private.py → omlish-0.0.0.dev6/omlish/inject/privates.py +6 -10
- omlish-0.0.0.dev6/omlish/inject/providers.py +38 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/scopes.py +20 -9
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/types.py +2 -8
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/iterators.py +13 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/__init__.py +12 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/cached.py +2 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/classes/restrict.py +3 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/classes/simple.py +18 -8
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/classes/virtual.py +2 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/contextmanagers.py +75 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/datetimes.py +6 -5
- omlish-0.0.0.dev6/omlish/lang/descriptors.py +228 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/functions.py +18 -28
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/imports.py +11 -2
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/iterables.py +20 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/typing.py +6 -0
- omlish-0.0.0.dev6/omlish/lifecycles/__init__.py +34 -0
- omlish-0.0.0.dev6/omlish/lifecycles/abstract.py +43 -0
- omlish-0.0.0.dev6/omlish/lifecycles/base.py +51 -0
- omlish-0.0.0.dev6/omlish/lifecycles/contextmanagers.py +74 -0
- omlish-0.0.0.dev6/omlish/lifecycles/controller.py +116 -0
- omlish-0.0.0.dev6/omlish/lifecycles/manager.py +161 -0
- omlish-0.0.0.dev6/omlish/lifecycles/states.py +43 -0
- omlish-0.0.0.dev6/omlish/lifecycles/transitions.py +64 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/formatters.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/utils.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/__init__.py +4 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/datetimes.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/naming.py +4 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/objects.py +1 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/polymorphism.py +4 -4
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/reflect.py +139 -18
- omlish-0.0.0.dev6/omlish/secrets/__init__.py +7 -0
- omlish-0.0.0.dev6/omlish/secrets/marshal.py +41 -0
- omlish-0.0.0.dev6/omlish/secrets/passwords.py +120 -0
- omlish-0.0.0.dev6/omlish/secrets/secrets.py +47 -0
- omlish-0.0.0.dev6/omlish/serde/dotenv.py +574 -0
- {omlish-0.0.0.dev4/omlish → omlish-0.0.0.dev6/omlish/serde}/json.py +4 -2
- omlish-0.0.0.dev6/omlish/serde/props.py +604 -0
- omlish-0.0.0.dev6/omlish/serde/yaml.py +223 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/sql/dbs.py +1 -1
- omlish-0.0.0.dev6/omlish/sql/duckdb.py +136 -0
- omlish-0.0.0.dev6/omlish/sql/sqlean.py +17 -0
- omlish-0.0.0.dev6/omlish/sync.py +70 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/term.py +7 -2
- omlish-0.0.0.dev6/omlish/testing/pytest/__init__.py +14 -0
- omlish-0.0.0.dev6/omlish/testing/pytest/helpers.py +11 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/inject/harness.py +4 -4
- omlish-0.0.0.dev4/omlish/testing/pytest/helpers.py → omlish-0.0.0.dev6/omlish/testing/pytest/marks.py +19 -9
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/__init__.py +3 -0
- omlish-0.0.0.dev6/omlish/testing/pytest/plugins/asyncs.py +136 -0
- omlish-0.0.0.dev6/omlish/testing/pytest/plugins/managermarks.py +60 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/pydevd.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/testing.py +10 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/text/delimit.py +4 -0
- omlish-0.0.0.dev6/omlish/text/glyphsplit.py +92 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6/omlish.egg-info}/PKG-INFO +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish.egg-info/SOURCES.txt +51 -6
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/pyproject.toml +29 -15
- omlish-0.0.0.dev4/omlish/asyncs/anyio.py +0 -86
- omlish-0.0.0.dev4/omlish/asyncs/asyncio.py +0 -19
- omlish-0.0.0.dev4/omlish/asyncs/trio_asyncio.py +0 -41
- omlish-0.0.0.dev4/omlish/configs/props.py +0 -64
- omlish-0.0.0.dev4/omlish/dataclasses/impl/exceptions.py +0 -2
- omlish-0.0.0.dev4/omlish/http/consts.py +0 -20
- omlish-0.0.0.dev4/omlish/inject/binder.py +0 -12
- omlish-0.0.0.dev4/omlish/inject/bindings.py +0 -49
- omlish-0.0.0.dev4/omlish/inject/elements.py +0 -43
- omlish-0.0.0.dev4/omlish/inject/impl/__init__.py +0 -0
- omlish-0.0.0.dev4/omlish/inject/inspect.py +0 -14
- omlish-0.0.0.dev4/omlish/inject/keys.py +0 -43
- omlish-0.0.0.dev4/omlish/inject/managed.py +0 -24
- omlish-0.0.0.dev4/omlish/inject/providers.py +0 -111
- omlish-0.0.0.dev4/omlish/lang/descriptors.py +0 -97
- omlish-0.0.0.dev4/omlish/testing/pytest/__init__.py +0 -8
- omlish-0.0.0.dev4/omlish/text/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/LICENSE +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/MANIFEST.in +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/README.md +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/argparse.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/asyncs/futures.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/asyncs/trio.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/check.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/_abc.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/_io_abc.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/cache/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/cache/descriptor.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/cache/types.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/coerce.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/frozen.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/indexed.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/mappings.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/ordered.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/persistent.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/skiplist.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/sorted.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/treap.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/treapmap.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/collections/unmodifiable.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/configs/flattening.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/as_.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/frozen.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/hashing.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/internals.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/metadata.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/order.py +1 -1
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/params.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/processing.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/replace.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/repr.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/simple.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/slots.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dataclasses/impl/utils.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/ps.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/replserver/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/diag/replserver/__main__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dispatch/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dispatch/dispatch.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dispatch/functions.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dispatch/methods.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/dynamic.py +0 -0
- {omlish-0.0.0.dev4/omlish/configs → omlish-0.0.0.dev6/omlish/graphs}/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/graphs/dot/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/graphs/dot/items.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/graphs/dot/rendering.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/graphs/dot/utils.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/http/wsgi.py +0 -0
- {omlish-0.0.0.dev4/omlish/diag → omlish-0.0.0.dev6/omlish/inject/impl}/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/inject/impl/bindings.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/classes/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/classes/abstract.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/clsdct.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/cmp.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/exceptions.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/maybes.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/objects.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/resolving.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/strings.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/sys.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/lang/timeouts.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/libc.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/_abc.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/configs.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/logs/filters.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/any.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/base.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/base64.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/dataclasses.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/enums.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/exceptions.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/factories.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/global_.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/iterables.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/mappings.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/optionals.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/primitives.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/registries.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/standard.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/utils.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/uuids.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/marshal/values.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/math.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/os.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/runmodule.py +0 -0
- {omlish-0.0.0.dev4/omlish/graphs → omlish-0.0.0.dev6/omlish/serde}/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/sql/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/sql/_abc.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/sql/asyncs.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/stats.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/inject/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/_registry.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/logging.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/repeat.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/skips.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/spacing.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/testing/pytest/plugins/switches.py +0 -0
- {omlish-0.0.0.dev4/omlish/http → omlish-0.0.0.dev6/omlish/text}/__init__.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/text/indent.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish/text/parts.py +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish.egg-info/dependency_links.txt +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish.egg-info/requires.txt +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/omlish.egg-info/top_level.txt +0 -0
- {omlish-0.0.0.dev4 → omlish-0.0.0.dev6}/setup.cfg +0 -0
|
@@ -6,6 +6,15 @@ from .asyncs import ( # noqa
|
|
|
6
6
|
syncable_iterable,
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
+
from .bridge import ( # noqa
|
|
10
|
+
a_to_s,
|
|
11
|
+
is_in_bridge,
|
|
12
|
+
s_to_a,
|
|
13
|
+
s_to_a_await,
|
|
14
|
+
trivial_a_to_s,
|
|
15
|
+
trivial_s_to_a,
|
|
16
|
+
)
|
|
17
|
+
|
|
9
18
|
from .flavors import ( # noqa
|
|
10
19
|
ContextManagerAdapter,
|
|
11
20
|
Flavor,
|
|
@@ -22,6 +31,7 @@ from .flavors import ( # noqa
|
|
|
22
31
|
mark_asyncio,
|
|
23
32
|
mark_flavor,
|
|
24
33
|
mark_trio,
|
|
34
|
+
with_adapter_loop,
|
|
25
35
|
)
|
|
26
36
|
|
|
27
37
|
from .futures import ( # noqa
|
|
@@ -32,7 +42,3 @@ from .futures import ( # noqa
|
|
|
32
42
|
wait_dependent_futures,
|
|
33
43
|
wait_futures,
|
|
34
44
|
)
|
|
35
|
-
|
|
36
|
-
from .trio_asyncio import ( # noqa
|
|
37
|
-
with_trio_asyncio_loop,
|
|
38
|
-
)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- bane
|
|
4
|
+
- owned lock
|
|
5
|
+
- async once
|
|
6
|
+
|
|
7
|
+
lookit:
|
|
8
|
+
- https://github.com/davidbrochart/sqlite-anyio/blob/a3ba4c6ef0535b14a5a60071fcd6ed565a514963/sqlite_anyio/sqlite.py
|
|
9
|
+
- https://github.com/rafalkrupinski/ratelimit-anyio/blob/2910a8a3d6fa54ed17ee6ba457686c9f7a4c4beb/src/ratelimit_anyio/__init__.py
|
|
10
|
+
- https://github.com/nekitdev/async-extensions/tree/main/async_extensions
|
|
11
|
+
- https://github.com/kinnay/anynet/tree/master/anynet
|
|
12
|
+
- https://github.com/M-o-a-T/asyncscope
|
|
13
|
+
- https://github.com/M-o-a-T/aevent
|
|
14
|
+
- https://github.com/florimondmanca/aiometer
|
|
15
|
+
- https://github.com/sanitizers/octomachinery/blob/b36c3d3d49da813ac46e361424132955a4e99ac8/octomachinery/utils/asynctools.py
|
|
16
|
+
""" # noqa
|
|
17
|
+
import typing as ta
|
|
18
|
+
|
|
19
|
+
import anyio.streams.memory
|
|
20
|
+
import anyio.streams.stapled
|
|
21
|
+
import sniffio
|
|
22
|
+
|
|
23
|
+
from .. import check
|
|
24
|
+
from .. import lang
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
T = ta.TypeVar('T')
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
async def anyio_eof_to_empty(fn: ta.Callable[..., ta.Awaitable[T]], *args: ta.Any, **kwargs: ta.Any) -> T | bytes:
|
|
34
|
+
try:
|
|
35
|
+
return await fn(*args, **kwargs)
|
|
36
|
+
except anyio.EndOfStream:
|
|
37
|
+
return b''
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def gather(*fns: ta.Callable[..., ta.Awaitable[T]], take_first: bool = False) -> list[lang.Maybe[T]]:
|
|
41
|
+
results: list[lang.Maybe[T]] = [lang.empty()] * len(fns)
|
|
42
|
+
|
|
43
|
+
async def inner(fn, i):
|
|
44
|
+
results[i] = lang.just(await fn())
|
|
45
|
+
if take_first:
|
|
46
|
+
tg.cancel_scope.cancel()
|
|
47
|
+
|
|
48
|
+
async with anyio.create_task_group() as tg:
|
|
49
|
+
for i, fn in enumerate(fns):
|
|
50
|
+
tg.start_soon(inner, fn, i)
|
|
51
|
+
|
|
52
|
+
return results
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def first(*fns: ta.Callable[..., ta.Awaitable[T]], **kwargs: ta.Any) -> list[lang.Maybe[T]]:
|
|
56
|
+
return await gather(*fns, take_first=True, **kwargs)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def get_current_task() -> anyio.TaskInfo | None:
|
|
63
|
+
try:
|
|
64
|
+
return anyio.get_current_task()
|
|
65
|
+
except sniffio.AsyncLibraryNotFoundError:
|
|
66
|
+
return None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
#
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
BackendTask: ta.TypeAlias = ta.Union[ # noqa
|
|
73
|
+
# asyncio.tasks.Task,
|
|
74
|
+
# trio.lowlevel.Task,
|
|
75
|
+
ta.Any,
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _is_class_named(obj: ta.Any, m: str, n: str) -> bool:
|
|
80
|
+
cls = obj.__class__
|
|
81
|
+
return cls.__module__ == m and cls.__name__ == n
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def get_backend_task(at: anyio.TaskInfo) -> BackendTask | None:
|
|
85
|
+
if _is_class_named(at, 'anyio._backends._asyncio', 'AsyncIOTaskInfo'):
|
|
86
|
+
# https://github.com/agronholm/anyio/blob/8907964926a24461840eee0925d3f355e729f15d/src/anyio/_backends/_asyncio.py#L1846 # noqa
|
|
87
|
+
# weakref.ref
|
|
88
|
+
obj = at._task() # type: ignore # noqa
|
|
89
|
+
if obj is not None and not (
|
|
90
|
+
_is_class_named(obj, '_asyncio', 'Task') or
|
|
91
|
+
_is_class_named(obj, 'asyncio.tasks', 'Task')
|
|
92
|
+
):
|
|
93
|
+
raise TypeError(obj)
|
|
94
|
+
return obj
|
|
95
|
+
|
|
96
|
+
elif _is_class_named(at, 'anyio._backends._trio', 'TrioTaskInfo'):
|
|
97
|
+
# https://github.com/agronholm/anyio/blob/8907964926a24461840eee0925d3f355e729f15d/src/anyio/_backends/_trio.py#L850 # noqa
|
|
98
|
+
# weakref.proxy
|
|
99
|
+
# https://stackoverflow.com/a/62144308 :|
|
|
100
|
+
obj = at._task.__repr__.__self__ # type: ignore # noqa
|
|
101
|
+
if obj is not None and not _is_class_named(obj, 'trio.lowlevel', 'Task'):
|
|
102
|
+
raise TypeError(obj)
|
|
103
|
+
return obj
|
|
104
|
+
|
|
105
|
+
else:
|
|
106
|
+
raise TypeError(at)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_current_backend_task() -> BackendTask | None:
|
|
110
|
+
if (at := get_current_task()) is not None:
|
|
111
|
+
return get_backend_task(at)
|
|
112
|
+
else:
|
|
113
|
+
return None
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
##
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def split_memory_object_streams(
|
|
120
|
+
*args: anyio.create_memory_object_stream[T],
|
|
121
|
+
) -> tuple[
|
|
122
|
+
anyio.streams.memory.MemoryObjectSendStream[T],
|
|
123
|
+
anyio.streams.memory.MemoryObjectReceiveStream[T],
|
|
124
|
+
]:
|
|
125
|
+
[tup] = args
|
|
126
|
+
return tup
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
# FIXME: https://github.com/python/mypy/issues/15238
|
|
130
|
+
# FIXME: https://youtrack.jetbrains.com/issues?q=tag:%20%7BPEP%20695%7D
|
|
131
|
+
def create_memory_object_stream[T](max_buffer_size: float = 0) -> tuple[
|
|
132
|
+
anyio.streams.memory.MemoryObjectSendStream[T],
|
|
133
|
+
anyio.streams.memory.MemoryObjectReceiveStream[T],
|
|
134
|
+
]:
|
|
135
|
+
return anyio.create_memory_object_stream[T](max_buffer_size) # noqa
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def staple_memory_object_stream(
|
|
139
|
+
*args: anyio.create_memory_object_stream[T],
|
|
140
|
+
) -> anyio.streams.stapled.StapledObjectStream[T]:
|
|
141
|
+
send, receive = args
|
|
142
|
+
return anyio.streams.stapled.StapledObjectStream(
|
|
143
|
+
check.isinstance(send, anyio.streams.memory.MemoryObjectSendStream), # type: ignore
|
|
144
|
+
check.isinstance(receive, anyio.streams.memory.MemoryObjectReceiveStream), # type: ignore
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# FIXME: https://github.com/python/mypy/issues/15238
|
|
149
|
+
# FIXME: https://youtrack.jetbrains.com/issues?q=tag:%20%7BPEP%20695%7D
|
|
150
|
+
def staple_memory_object_stream2[T](max_buffer_size: float = 0) -> anyio.streams.stapled.StapledObjectStream[T]:
|
|
151
|
+
send, receive = anyio.create_memory_object_stream[T](max_buffer_size)
|
|
152
|
+
return anyio.streams.stapled.StapledObjectStream(
|
|
153
|
+
check.isinstance(send, anyio.streams.memory.MemoryObjectSendStream), # type: ignore
|
|
154
|
+
check.isinstance(receive, anyio.streams.memory.MemoryObjectReceiveStream), # type: ignore
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
##
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class Once:
|
|
162
|
+
def __init__(self) -> None:
|
|
163
|
+
super().__init__()
|
|
164
|
+
self._done = False
|
|
165
|
+
self._lock = anyio.Lock()
|
|
166
|
+
|
|
167
|
+
async def do(self, fn: ta.Callable[[], ta.Awaitable[None]]) -> bool:
|
|
168
|
+
if self._done:
|
|
169
|
+
return False
|
|
170
|
+
async with self._lock:
|
|
171
|
+
if self._done:
|
|
172
|
+
return False # type: ignore
|
|
173
|
+
try:
|
|
174
|
+
await fn()
|
|
175
|
+
finally:
|
|
176
|
+
self._done = True
|
|
177
|
+
return True
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class Lazy(ta.Generic[T]):
|
|
181
|
+
def __init__(self) -> None:
|
|
182
|
+
super().__init__()
|
|
183
|
+
self._once = Once()
|
|
184
|
+
self._v: lang.Maybe[T] = lang.empty()
|
|
185
|
+
|
|
186
|
+
def peek(self) -> lang.Maybe[T]:
|
|
187
|
+
return self._v
|
|
188
|
+
|
|
189
|
+
def set(self, v: T) -> None:
|
|
190
|
+
self._v = lang.just(v)
|
|
191
|
+
|
|
192
|
+
async def get(self, fn: ta.Callable[[], ta.Awaitable[T]]) -> T:
|
|
193
|
+
async def do():
|
|
194
|
+
self._v = lang.just(await fn())
|
|
195
|
+
await self._once.do(do)
|
|
196
|
+
return self._v.must()
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
class LazyFn(ta.Generic[T]):
|
|
200
|
+
def __init__(self, fn: ta.Callable[[], ta.Awaitable[T]]) -> None:
|
|
201
|
+
super().__init__()
|
|
202
|
+
self._fn = fn
|
|
203
|
+
self._once = Once()
|
|
204
|
+
self._v: lang.Maybe[T] = lang.empty()
|
|
205
|
+
|
|
206
|
+
def peek(self) -> lang.Maybe[T]:
|
|
207
|
+
return self._v
|
|
208
|
+
|
|
209
|
+
def set(self, v: T) -> None:
|
|
210
|
+
self._v = lang.just(v)
|
|
211
|
+
|
|
212
|
+
async def get(self) -> T:
|
|
213
|
+
async def do():
|
|
214
|
+
self._v = lang.just(await self._fn())
|
|
215
|
+
await self._once.do(do)
|
|
216
|
+
return self._v.must()
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import contextlib
|
|
3
|
+
import functools
|
|
4
|
+
import typing as ta
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
CallableT = ta.TypeVar('CallableT', bound=ta.Callable)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def asyncio_once(fn: CallableT) -> CallableT:
|
|
11
|
+
future = None
|
|
12
|
+
|
|
13
|
+
@functools.wraps(fn)
|
|
14
|
+
async def inner(*args, **kwargs):
|
|
15
|
+
nonlocal future
|
|
16
|
+
if not future:
|
|
17
|
+
future = asyncio.create_task(fn(*args, **kwargs))
|
|
18
|
+
return await future
|
|
19
|
+
|
|
20
|
+
return ta.cast(CallableT, inner)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def get_real_current_loop() -> asyncio.AbstractEventLoop | None:
|
|
24
|
+
return asyncio.get_event_loop_policy()._local._loop # type: ignore # noqa
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def drain_tasks(loop=None):
|
|
28
|
+
if loop is None:
|
|
29
|
+
loop = get_real_current_loop()
|
|
30
|
+
|
|
31
|
+
while loop._ready or loop._scheduled: # noqa
|
|
32
|
+
loop._run_once() # noqa
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@contextlib.contextmanager
|
|
36
|
+
def draining_asyncio_tasks() -> ta.Iterator[None]:
|
|
37
|
+
loop = get_real_current_loop()
|
|
38
|
+
try:
|
|
39
|
+
yield
|
|
40
|
+
finally:
|
|
41
|
+
if loop is not None:
|
|
42
|
+
drain_tasks(loop) # noqa
|
|
@@ -5,10 +5,10 @@ TODO:
|
|
|
5
5
|
517 ns ± 13.2 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
|
|
6
6
|
- injected io provider - sync vs greenlet aio trampolined
|
|
7
7
|
- push/pull bridge?
|
|
8
|
+
- move to lang
|
|
8
9
|
|
|
9
10
|
https://github.com/sqlalchemy/sqlalchemy/blob/1e75c189da721395bc8c2d899c722a5b9a170404/lib/sqlalchemy/util/_concurrency_py3k.py#L83
|
|
10
11
|
"""
|
|
11
|
-
import contextlib
|
|
12
12
|
import functools
|
|
13
13
|
import typing as ta
|
|
14
14
|
|
|
@@ -16,8 +16,7 @@ import typing as ta
|
|
|
16
16
|
T = ta.TypeVar('T')
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def sync_await(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
20
|
-
ret: ta.Any
|
|
19
|
+
def sync_await(fn: ta.Callable[..., T], *args: ta.Any, **kwargs: ta.Any) -> T:
|
|
21
20
|
ret = missing = object()
|
|
22
21
|
|
|
23
22
|
async def gate():
|
|
@@ -25,13 +24,17 @@ def sync_await(fn: ta.Callable[..., T], *args, **kwargs) -> T:
|
|
|
25
24
|
ret = await fn(*args, **kwargs) # type: ignore
|
|
26
25
|
|
|
27
26
|
cr = gate()
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
try:
|
|
28
|
+
try:
|
|
30
29
|
cr.send(None)
|
|
30
|
+
except StopIteration:
|
|
31
|
+
pass
|
|
31
32
|
if ret is missing or cr.cr_await is not None or cr.cr_running:
|
|
32
33
|
raise TypeError('Not terminated')
|
|
34
|
+
finally:
|
|
35
|
+
cr.close()
|
|
33
36
|
|
|
34
|
-
return
|
|
37
|
+
return ret # type: ignore
|
|
35
38
|
|
|
36
39
|
|
|
37
40
|
def sync_list(fn: ta.Callable[..., ta.AsyncIterator[T]], *args, **kwargs) -> list[T]:
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"""
|
|
2
|
+
https://github.com/kubernetes/kubernetes/blob/60c4c2b2521fb454ce69dee737e3eb91a25e0535/pkg/controller/volume/persistentvolume/pv_controller.go#L60-L63
|
|
3
|
+
|
|
4
|
+
==================================================================
|
|
5
|
+
PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE.
|
|
6
|
+
KEEP THE SPACE SHUTTLE FLYING.
|
|
7
|
+
==================================================================
|
|
8
|
+
|
|
9
|
+
TODO:
|
|
10
|
+
- reuse greenlet if nested somehow?
|
|
11
|
+
"""
|
|
12
|
+
import itertools
|
|
13
|
+
import sys
|
|
14
|
+
import types
|
|
15
|
+
import typing as ta
|
|
16
|
+
import weakref
|
|
17
|
+
|
|
18
|
+
from .. import lang
|
|
19
|
+
from .asyncs import sync_await
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if ta.TYPE_CHECKING:
|
|
23
|
+
import asyncio
|
|
24
|
+
|
|
25
|
+
import greenlet
|
|
26
|
+
|
|
27
|
+
from . import anyio as aiu
|
|
28
|
+
|
|
29
|
+
else:
|
|
30
|
+
asyncio = lang.proxy_import('asyncio')
|
|
31
|
+
|
|
32
|
+
greenlet = lang.proxy_import('greenlet')
|
|
33
|
+
|
|
34
|
+
aiu = lang.proxy_import('.anyio', __package__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
T = ta.TypeVar('T')
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
##
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def trivial_s_to_a(fn):
|
|
44
|
+
async def inner(*args, **kwargs):
|
|
45
|
+
return fn(*args, **kwargs)
|
|
46
|
+
return inner
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def trivial_a_to_s(fn):
|
|
50
|
+
def inner(*args, **kwargs):
|
|
51
|
+
return sync_await(fn, *args, **kwargs)
|
|
52
|
+
return inner
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
##
|
|
56
|
+
# https://gist.github.com/snaury/202bf4f22c41ca34e56297bae5f33fef
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class BridgeAwaitRequiredError(Exception):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class MissingBridgeGreenletError(Exception):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class UnexpectedBridgeNestingError(Exception):
|
|
68
|
+
def __init__(self, *args, **kwargs):
|
|
69
|
+
super().__init__(*args, **kwargs)
|
|
70
|
+
# breakpoint()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
#
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
_DEBUG_PRINT: ta.Callable[..., None] | None = None
|
|
77
|
+
# _DEBUG_PRINT = print # noqa
|
|
78
|
+
|
|
79
|
+
_TRACK_TRANSITION_OBJS = False
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
#
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
_BRIDGE_TRANSITIONS_SEQ = itertools.count()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class _BridgeTransition(ta.NamedTuple):
|
|
89
|
+
seq: int
|
|
90
|
+
a_to_s: bool
|
|
91
|
+
|
|
92
|
+
obj_cls: type
|
|
93
|
+
obj_id: int
|
|
94
|
+
|
|
95
|
+
obj: ta.Any
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _make_transition(seq: int, a_to_s: bool, obj: ta.Any) -> _BridgeTransition:
|
|
99
|
+
return _BridgeTransition(seq, a_to_s, obj.__class__, id(obj), (obj if _TRACK_TRANSITION_OBJS else None))
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
_BRIDGED_TASKS: ta.MutableMapping[ta.Any, list[_BridgeTransition]] = weakref.WeakKeyDictionary()
|
|
103
|
+
|
|
104
|
+
_BRIDGE_GREENLET_ATTR = f'__{__package__.replace(".", "__")}__bridge_greenlet__'
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _push_transition(a_to_s: bool, l: list[_BridgeTransition], t: _BridgeTransition) -> _BridgeTransition:
|
|
108
|
+
l.append(t)
|
|
109
|
+
if _DEBUG_PRINT:
|
|
110
|
+
_DEBUG_PRINT(f'_push_transition: {a_to_s=} {id(l)=} {t=}')
|
|
111
|
+
return t
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _pop_transition(a_to_s: bool, l: list[_BridgeTransition]) -> _BridgeTransition:
|
|
115
|
+
t = l.pop()
|
|
116
|
+
if _DEBUG_PRINT:
|
|
117
|
+
_DEBUG_PRINT(f'_pop_transition: {a_to_s=} {id(l)=} {t=}')
|
|
118
|
+
return t
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _get_transitions() -> list[_BridgeTransition]:
|
|
122
|
+
l: list[_BridgeTransition] = []
|
|
123
|
+
|
|
124
|
+
if (t := aiu.get_current_backend_task()) is not None:
|
|
125
|
+
try:
|
|
126
|
+
tl = _BRIDGED_TASKS[t]
|
|
127
|
+
except KeyError:
|
|
128
|
+
pass
|
|
129
|
+
else:
|
|
130
|
+
l.extend(tl)
|
|
131
|
+
|
|
132
|
+
g = greenlet.getcurrent()
|
|
133
|
+
try:
|
|
134
|
+
gl = getattr(g, _BRIDGE_GREENLET_ATTR)
|
|
135
|
+
except AttributeError:
|
|
136
|
+
pass
|
|
137
|
+
else:
|
|
138
|
+
l.extend(gl)
|
|
139
|
+
|
|
140
|
+
l.sort(key=lambda t: (t.seq, t.a_to_s))
|
|
141
|
+
return l
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def is_in_bridge() -> bool:
|
|
145
|
+
if _DEBUG_PRINT:
|
|
146
|
+
_DEBUG_PRINT(_get_transitions())
|
|
147
|
+
|
|
148
|
+
if (t := aiu.get_current_backend_task()) is not None:
|
|
149
|
+
try:
|
|
150
|
+
tl = _BRIDGED_TASKS[t]
|
|
151
|
+
except KeyError:
|
|
152
|
+
last_t = None
|
|
153
|
+
else:
|
|
154
|
+
if tl:
|
|
155
|
+
last_t = tl[-1]
|
|
156
|
+
else:
|
|
157
|
+
last_t = None
|
|
158
|
+
else:
|
|
159
|
+
last_t = None
|
|
160
|
+
|
|
161
|
+
g = greenlet.getcurrent()
|
|
162
|
+
try:
|
|
163
|
+
gl = getattr(g, _BRIDGE_GREENLET_ATTR)
|
|
164
|
+
except AttributeError:
|
|
165
|
+
last_g = None
|
|
166
|
+
else:
|
|
167
|
+
if gl:
|
|
168
|
+
last_g = gl[-1]
|
|
169
|
+
else:
|
|
170
|
+
last_g = None
|
|
171
|
+
|
|
172
|
+
if last_t is None:
|
|
173
|
+
if last_g is None:
|
|
174
|
+
return False
|
|
175
|
+
o = last_g
|
|
176
|
+
else: # noqa
|
|
177
|
+
if last_g is None or last_g.seq < last_t.seq:
|
|
178
|
+
o = last_t
|
|
179
|
+
else:
|
|
180
|
+
o = last_g
|
|
181
|
+
|
|
182
|
+
in_a = (t is not None)
|
|
183
|
+
|
|
184
|
+
if _DEBUG_PRINT:
|
|
185
|
+
_DEBUG_PRINT(f'{o.a_to_s=} {in_a=}')
|
|
186
|
+
|
|
187
|
+
return in_a != o.a_to_s
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _safe_cancel_awaitable(awaitable: ta.Awaitable[ta.Any]) -> None:
|
|
191
|
+
# https://docs.python.org/3/reference/datamodel.html#coroutine.close
|
|
192
|
+
if asyncio.iscoroutine(awaitable):
|
|
193
|
+
awaitable.close() # noqa
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def s_to_a_await(awaitable: ta.Awaitable[T]) -> T:
|
|
197
|
+
g = greenlet.getcurrent()
|
|
198
|
+
|
|
199
|
+
if not getattr(g, _BRIDGE_GREENLET_ATTR, False):
|
|
200
|
+
_safe_cancel_awaitable(awaitable)
|
|
201
|
+
raise MissingBridgeGreenletError
|
|
202
|
+
|
|
203
|
+
return g.parent.switch(awaitable)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def s_to_a(fn, *, require_await=False):
|
|
207
|
+
@types.coroutine
|
|
208
|
+
def outer(*args, **kwargs):
|
|
209
|
+
def inner():
|
|
210
|
+
try:
|
|
211
|
+
return fn(*args, **kwargs)
|
|
212
|
+
finally:
|
|
213
|
+
if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
|
|
214
|
+
raise UnexpectedBridgeNestingError
|
|
215
|
+
if (cur_g := _pop_transition(False, gl)) is not added_g: # noqa
|
|
216
|
+
raise UnexpectedBridgeNestingError
|
|
217
|
+
if gl:
|
|
218
|
+
raise UnexpectedBridgeNestingError
|
|
219
|
+
|
|
220
|
+
seq = next(_BRIDGE_TRANSITIONS_SEQ)
|
|
221
|
+
|
|
222
|
+
g = greenlet.greenlet(inner)
|
|
223
|
+
setattr(g, _BRIDGE_GREENLET_ATTR, gl := []) # type: ignore
|
|
224
|
+
added_g = _push_transition(False, gl, _make_transition(seq, False, g))
|
|
225
|
+
|
|
226
|
+
if (t := aiu.get_current_backend_task()) is not None:
|
|
227
|
+
try:
|
|
228
|
+
tl = _BRIDGED_TASKS[t]
|
|
229
|
+
except KeyError:
|
|
230
|
+
tl = _BRIDGED_TASKS[t] = []
|
|
231
|
+
added_t = _push_transition(False, tl, _make_transition(seq, False, g))
|
|
232
|
+
|
|
233
|
+
try:
|
|
234
|
+
result: ta.Any = g.switch()
|
|
235
|
+
switch_occurred = False
|
|
236
|
+
while not g.dead:
|
|
237
|
+
switch_occurred = True
|
|
238
|
+
try:
|
|
239
|
+
value = yield result
|
|
240
|
+
except BaseException: # noqa
|
|
241
|
+
result = g.throw(*sys.exc_info())
|
|
242
|
+
else:
|
|
243
|
+
result = g.switch(value)
|
|
244
|
+
|
|
245
|
+
if require_await and not switch_occurred:
|
|
246
|
+
raise BridgeAwaitRequiredError
|
|
247
|
+
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
finally:
|
|
251
|
+
if t is not None:
|
|
252
|
+
if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
|
|
253
|
+
raise UnexpectedBridgeNestingError
|
|
254
|
+
if (cur_t := _pop_transition(False, tl)) is not added_t: # noqa
|
|
255
|
+
raise UnexpectedBridgeNestingError
|
|
256
|
+
|
|
257
|
+
return outer
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def a_to_s(fn):
|
|
261
|
+
def inner(*args, **kwargs):
|
|
262
|
+
seq = next(_BRIDGE_TRANSITIONS_SEQ)
|
|
263
|
+
|
|
264
|
+
if (t := aiu.get_current_backend_task()) is not None:
|
|
265
|
+
try:
|
|
266
|
+
tl = _BRIDGED_TASKS[t]
|
|
267
|
+
except KeyError:
|
|
268
|
+
tl = _BRIDGED_TASKS[t] = []
|
|
269
|
+
added_t = _push_transition(True, tl, _make_transition(seq, True, t))
|
|
270
|
+
else:
|
|
271
|
+
added_t = None
|
|
272
|
+
|
|
273
|
+
g = greenlet.getcurrent()
|
|
274
|
+
try:
|
|
275
|
+
gl = getattr(g, _BRIDGE_GREENLET_ATTR)
|
|
276
|
+
except AttributeError:
|
|
277
|
+
setattr(g, _BRIDGE_GREENLET_ATTR, gl := [])
|
|
278
|
+
added_g = _push_transition(True, gl, _make_transition(seq, True, g))
|
|
279
|
+
|
|
280
|
+
try:
|
|
281
|
+
ret = missing = object()
|
|
282
|
+
|
|
283
|
+
async def gate():
|
|
284
|
+
nonlocal ret
|
|
285
|
+
ret = await fn(*args, **kwargs)
|
|
286
|
+
|
|
287
|
+
cr = gate()
|
|
288
|
+
sv = None
|
|
289
|
+
try:
|
|
290
|
+
while True:
|
|
291
|
+
try:
|
|
292
|
+
sv = cr.send(sv)
|
|
293
|
+
except StopIteration:
|
|
294
|
+
break
|
|
295
|
+
|
|
296
|
+
if ret is missing or cr.cr_await is not None or cr.cr_running:
|
|
297
|
+
sv = s_to_a_await(sv)
|
|
298
|
+
|
|
299
|
+
finally:
|
|
300
|
+
cr.close()
|
|
301
|
+
|
|
302
|
+
finally:
|
|
303
|
+
if t is not None:
|
|
304
|
+
if (tl2 := _BRIDGED_TASKS[t]) is not tl: # noqa
|
|
305
|
+
raise UnexpectedBridgeNestingError
|
|
306
|
+
if (cur_t := _pop_transition(True, tl)) is not added_t: # noqa
|
|
307
|
+
raise UnexpectedBridgeNestingError
|
|
308
|
+
|
|
309
|
+
if (gl2 := getattr(g, _BRIDGE_GREENLET_ATTR)) is not gl: # noqa
|
|
310
|
+
raise UnexpectedBridgeNestingError
|
|
311
|
+
if (cur_g := _pop_transition(True, gl)) is not added_g: # noqa
|
|
312
|
+
raise UnexpectedBridgeNestingError
|
|
313
|
+
|
|
314
|
+
return ret
|
|
315
|
+
|
|
316
|
+
return inner
|