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/lang/imports/proxy.py
CHANGED
|
@@ -1,10 +1,28 @@
|
|
|
1
1
|
"""
|
|
2
2
|
TODO:
|
|
3
3
|
- if already imported just return?
|
|
4
|
+
- no, need sub-imports..
|
|
5
|
+
- seal on first use? or just per module? can't seal roots and still be usable
|
|
6
|
+
- only if not hasattr?
|
|
7
|
+
- audit for deadlock risk - does importlib._bootstrap do it for us? do we need a global _ProxyImporter lock? would only
|
|
8
|
+
happen on reification
|
|
9
|
+
- ProxyImportError
|
|
10
|
+
- detect import reification in own module body - user is failing to properly 'hands-off' lazy import
|
|
11
|
+
- bonus points detect when done specifically for a type annotation
|
|
12
|
+
|
|
13
|
+
See:
|
|
14
|
+
- https://peps.python.org/pep-0810/
|
|
15
|
+
- https://github.com/LazyImportsCabal/cpython/tree/lazy
|
|
16
|
+
- https://developers.facebook.com/blog/post/2022/06/15/python-lazy-imports-with-cinder/
|
|
17
|
+
- https://engineering.fb.com/2024/01/18/developer-tools/lazy-imports-cinder-machine-learning-meta/
|
|
18
|
+
- https://www.hudsonrivertrading.com/hrtbeat/inside-hrts-python-fork/
|
|
19
|
+
- https://bugreports.qt.io/browse/PYSIDE-2404
|
|
20
|
+
- https://scientific-python.org/specs/spec-0001/
|
|
21
|
+
- https://github.com/scientific-python/lazy-loader
|
|
4
22
|
"""
|
|
5
|
-
import contextlib
|
|
6
23
|
import functools
|
|
7
24
|
import importlib.util
|
|
25
|
+
import threading
|
|
8
26
|
import types
|
|
9
27
|
import typing as ta
|
|
10
28
|
|
|
@@ -13,138 +31,349 @@ from .capture import ImportCapture
|
|
|
13
31
|
from .capture import _new_import_capture_hook
|
|
14
32
|
|
|
15
33
|
|
|
34
|
+
_ProxyImporterModuleAttr: ta.TypeAlias = ta.Literal[
|
|
35
|
+
'child', # 'outranks' proxy_attr - all child attrs must be proxy_attrs but not vice versa
|
|
36
|
+
'proxy_attr',
|
|
37
|
+
'pending_child',
|
|
38
|
+
'pending_attr',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
16
42
|
##
|
|
17
43
|
|
|
18
44
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
45
|
+
class _ProxyImporter:
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
owner_globals: ta.MutableMapping[str, ta.Any] | None = None,
|
|
50
|
+
) -> None:
|
|
51
|
+
super().__init__()
|
|
26
52
|
|
|
27
|
-
|
|
53
|
+
self._owner_globals = owner_globals
|
|
54
|
+
|
|
55
|
+
self._owner_name: str | None = owner_globals.get('__name__') if owner_globals else None
|
|
56
|
+
|
|
57
|
+
# NOTE: Import machinery may be reentrant for things like gc ops and signal handling:
|
|
58
|
+
# TODO: audit for reentrancy this lol
|
|
59
|
+
# https://github.com/python/cpython/blob/72f25a8d9a5673d39c107cf522465a566b979ed5/Lib/importlib/_bootstrap.py#L233-L237 # noqa
|
|
60
|
+
self._lock = threading.RLock()
|
|
61
|
+
|
|
62
|
+
self._modules_by_name: dict[str, _ProxyImporter._Module] = {}
|
|
63
|
+
self._modules_by_proxy_obj: dict[types.ModuleType, _ProxyImporter._Module] = {}
|
|
64
|
+
|
|
65
|
+
class _Module:
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
name: str,
|
|
69
|
+
getattr_handler: ta.Callable[['_ProxyImporter._Module', str], ta.Any],
|
|
70
|
+
*,
|
|
71
|
+
parent: ta.Optional['_ProxyImporter._Module'] = None,
|
|
72
|
+
) -> None:
|
|
73
|
+
super().__init__()
|
|
74
|
+
|
|
75
|
+
self.name = name
|
|
76
|
+
self.parent = parent
|
|
77
|
+
|
|
78
|
+
self.base_name = name.rpartition('.')[2]
|
|
79
|
+
self.root: _ProxyImporter._Module = parent.root if parent is not None else self # noqa
|
|
80
|
+
|
|
81
|
+
self.children: dict[str, _ProxyImporter._Module] = {}
|
|
82
|
+
self.descendants: set[_ProxyImporter._Module] = set()
|
|
83
|
+
|
|
84
|
+
self.proxy_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {name}>')
|
|
85
|
+
self.proxy_obj.__file__ = None
|
|
86
|
+
self.proxy_obj.__getattr__ = functools.partial(getattr_handler, self) # type: ignore[method-assign]
|
|
87
|
+
|
|
88
|
+
self.pending_children: set[str] = set()
|
|
89
|
+
self.pending_attrs: set[str] = set()
|
|
90
|
+
|
|
91
|
+
real_obj: types.ModuleType | None = None
|
|
92
|
+
|
|
93
|
+
def __repr__(self) -> str:
|
|
94
|
+
return f'{self.__class__.__name__}<{self.name}{"!" if self.real_obj is not None else ""}>'
|
|
95
|
+
|
|
96
|
+
def find_attr(self, attr: str) -> _ProxyImporterModuleAttr | None:
|
|
97
|
+
is_child = attr in self.children
|
|
98
|
+
is_proxy_attr = attr in self.proxy_obj.__dict__
|
|
99
|
+
is_pending_child = attr in self.pending_children
|
|
100
|
+
is_pending_attr = attr in self.pending_attrs
|
|
101
|
+
|
|
102
|
+
if is_child:
|
|
103
|
+
if (
|
|
104
|
+
not is_proxy_attr or
|
|
105
|
+
is_pending_child or
|
|
106
|
+
is_pending_attr
|
|
107
|
+
):
|
|
108
|
+
raise RuntimeError
|
|
109
|
+
return 'child'
|
|
110
|
+
|
|
111
|
+
elif is_proxy_attr:
|
|
112
|
+
if (
|
|
113
|
+
is_pending_child or
|
|
114
|
+
is_pending_attr
|
|
115
|
+
):
|
|
116
|
+
raise RuntimeError
|
|
117
|
+
return 'proxy_attr'
|
|
118
|
+
|
|
119
|
+
elif is_pending_child:
|
|
120
|
+
if (
|
|
121
|
+
is_child or
|
|
122
|
+
is_proxy_attr or
|
|
123
|
+
is_pending_attr
|
|
124
|
+
):
|
|
125
|
+
raise RuntimeError
|
|
126
|
+
return 'pending_child'
|
|
127
|
+
|
|
128
|
+
elif is_pending_attr:
|
|
129
|
+
if (
|
|
130
|
+
is_child or
|
|
131
|
+
is_proxy_attr or
|
|
132
|
+
is_pending_child
|
|
133
|
+
):
|
|
134
|
+
raise RuntimeError
|
|
135
|
+
return 'pending_attr'
|
|
28
136
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if omod is None:
|
|
32
|
-
omod = importlib.import_module(spec, package=package)
|
|
33
|
-
if extras:
|
|
34
|
-
for x in extras:
|
|
35
|
-
importlib.import_module(f'{spec}.{x}', package=package)
|
|
36
|
-
return getattr(omod, att)
|
|
137
|
+
else:
|
|
138
|
+
return None
|
|
37
139
|
|
|
38
|
-
|
|
39
|
-
lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
|
|
40
|
-
return lmod
|
|
140
|
+
#
|
|
41
141
|
|
|
142
|
+
def _get_or_make_module_locked(self, name: str) -> _Module:
|
|
143
|
+
try:
|
|
144
|
+
return self._modules_by_name[name]
|
|
145
|
+
except KeyError:
|
|
146
|
+
pass
|
|
42
147
|
|
|
43
|
-
|
|
148
|
+
parent: _ProxyImporter._Module | None = None
|
|
149
|
+
if '.' in name:
|
|
150
|
+
rest, _, attr = name.rpartition('.')
|
|
151
|
+
parent = self._get_or_make_module_locked(rest)
|
|
44
152
|
|
|
153
|
+
fa = parent.find_attr(attr)
|
|
154
|
+
if not (fa == 'pending_child' or fa is None):
|
|
155
|
+
raise RuntimeError
|
|
45
156
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
*,
|
|
49
|
-
disable: bool = False,
|
|
157
|
+
if (ro := parent.real_obj) is not None and attr not in ro.__dict__:
|
|
158
|
+
raise NotImplementedError
|
|
50
159
|
|
|
51
|
-
|
|
52
|
-
|
|
160
|
+
module = self._modules_by_name[name] = _ProxyImporter._Module(
|
|
161
|
+
name,
|
|
162
|
+
self._handle_module_getattr,
|
|
163
|
+
)
|
|
53
164
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
inst = ImportCapture(
|
|
57
|
-
mod_globals,
|
|
58
|
-
_hook=_new_import_capture_hook(
|
|
59
|
-
mod_globals,
|
|
60
|
-
stack_offset=_stack_offset + 1,
|
|
61
|
-
),
|
|
62
|
-
disable=disable,
|
|
63
|
-
)
|
|
165
|
+
self._modules_by_name[name] = module
|
|
166
|
+
self._modules_by_proxy_obj[module.proxy_obj] = module
|
|
64
167
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
):
|
|
71
|
-
yield inst
|
|
168
|
+
if parent is not None:
|
|
169
|
+
parent.pending_children.discard(module.base_name)
|
|
170
|
+
parent.children[module.base_name] = module
|
|
171
|
+
setattr(parent.proxy_obj, module.base_name, module.proxy_obj)
|
|
172
|
+
parent.root.descendants.add(module)
|
|
72
173
|
|
|
73
|
-
|
|
74
|
-
for pi in inst.captured.imports:
|
|
75
|
-
for sa, ma in pi.attrs:
|
|
76
|
-
mod_globals[ma] = proxy_import(pi.spec + (('.' + sa) if sa is not None else ''), pkg)
|
|
174
|
+
return module
|
|
77
175
|
|
|
78
|
-
|
|
176
|
+
def _extend_module_locked(
|
|
177
|
+
self,
|
|
178
|
+
module: _Module,
|
|
179
|
+
*,
|
|
180
|
+
children: ta.Iterable[str] | None = None,
|
|
181
|
+
attrs: ta.Iterable[str] | None = None,
|
|
182
|
+
) -> None:
|
|
183
|
+
for l in (children, attrs):
|
|
184
|
+
for n in l or ():
|
|
185
|
+
if n in module.proxy_obj.__dict__:
|
|
186
|
+
raise NotImplementedError
|
|
79
187
|
|
|
188
|
+
if (ro := module.real_obj) is not None and n in ro.__dict__:
|
|
189
|
+
raise NotImplementedError
|
|
80
190
|
|
|
81
|
-
|
|
191
|
+
for n in children or ():
|
|
192
|
+
fa = module.find_attr(n)
|
|
193
|
+
if not (fa == 'pending_child' or fa is None):
|
|
194
|
+
raise RuntimeError
|
|
82
195
|
|
|
196
|
+
for n in attrs or ():
|
|
197
|
+
fa = module.find_attr(n)
|
|
198
|
+
if not (fa == 'pending_attr' or fa is None):
|
|
199
|
+
raise RuntimeError
|
|
83
200
|
|
|
84
|
-
|
|
85
|
-
class NamePackage(ta.NamedTuple):
|
|
86
|
-
name: str
|
|
87
|
-
package: str
|
|
201
|
+
#
|
|
88
202
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
203
|
+
if children:
|
|
204
|
+
module.pending_children.update(n for n in children if n not in module.children)
|
|
205
|
+
if attrs:
|
|
206
|
+
module.pending_attrs.update(attrs)
|
|
92
207
|
|
|
93
|
-
def
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
208
|
+
def _retrieve_from_module_locked(self, module: _Module, attr: str) -> ta.Any:
|
|
209
|
+
fa = module.find_attr(attr)
|
|
210
|
+
|
|
211
|
+
if fa == 'child' or fa == 'proxy_attr':
|
|
212
|
+
return module.proxy_obj.__dict__[attr]
|
|
213
|
+
|
|
214
|
+
val: ta.Any
|
|
215
|
+
|
|
216
|
+
if fa == 'pending_child':
|
|
217
|
+
if module.name == self._owner_name:
|
|
218
|
+
val = importlib.import_module(f'{module.name}.{attr}')
|
|
219
|
+
|
|
220
|
+
else:
|
|
221
|
+
mod = __import__(
|
|
222
|
+
module.name,
|
|
223
|
+
self._owner_globals or {},
|
|
224
|
+
{},
|
|
225
|
+
[attr],
|
|
226
|
+
0,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
val = getattr(mod, attr)
|
|
230
|
+
|
|
231
|
+
module.pending_children.remove(attr)
|
|
232
|
+
|
|
233
|
+
elif fa == 'pending_attr' or fa is None:
|
|
234
|
+
if module.name == self._owner_name:
|
|
235
|
+
raise NotImplementedError
|
|
99
236
|
|
|
100
|
-
|
|
101
|
-
|
|
237
|
+
if (ro := module.real_obj) is None:
|
|
238
|
+
ro = module.real_obj = importlib.import_module(module.name)
|
|
102
239
|
|
|
103
|
-
|
|
104
|
-
self._mods_by_spec: dict[str, ta.Any] = {}
|
|
240
|
+
val = getattr(ro, attr)
|
|
105
241
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
242
|
+
if fa == 'pending_attr':
|
|
243
|
+
module.pending_attrs.remove(attr)
|
|
244
|
+
|
|
245
|
+
else:
|
|
246
|
+
raise TypeError(fa)
|
|
247
|
+
|
|
248
|
+
setattr(module.proxy_obj, attr, val)
|
|
249
|
+
return val
|
|
250
|
+
|
|
251
|
+
#
|
|
252
|
+
|
|
253
|
+
def _handle_module_getattr(self, module: _Module, attr: str) -> ta.Any:
|
|
254
|
+
with self._lock:
|
|
255
|
+
return self._retrieve_from_module_locked(module, attr)
|
|
109
256
|
|
|
110
257
|
def add(
|
|
111
258
|
self,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
259
|
+
module: str,
|
|
260
|
+
*,
|
|
261
|
+
children: ta.Iterable[str] | None = None,
|
|
262
|
+
attrs: ta.Iterable[str] | None = None,
|
|
263
|
+
) -> types.ModuleType:
|
|
264
|
+
if isinstance(children, str):
|
|
265
|
+
raise TypeError(children)
|
|
266
|
+
|
|
267
|
+
# Leaf modules get collapsed into parents' pending_children
|
|
268
|
+
if not children and not attrs and '.' in module:
|
|
269
|
+
module, _, child = module.rpartition('.')
|
|
270
|
+
children = [child]
|
|
271
|
+
|
|
272
|
+
with self._lock:
|
|
273
|
+
m = self._get_or_make_module_locked(module)
|
|
274
|
+
|
|
275
|
+
if children or attrs:
|
|
276
|
+
self._extend_module_locked(
|
|
277
|
+
m,
|
|
278
|
+
children=children,
|
|
279
|
+
attrs=attrs,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
return m.proxy_obj
|
|
283
|
+
|
|
284
|
+
def get_module(self, name: str) -> types.ModuleType:
|
|
285
|
+
try:
|
|
286
|
+
return self._modules_by_name[name].proxy_obj
|
|
287
|
+
except KeyError:
|
|
288
|
+
pass
|
|
119
289
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
self._lazy_globals.set_fn(as_attr, functools.partial(self.get, as_attr))
|
|
290
|
+
with self._lock:
|
|
291
|
+
return self._get_or_make_module_locked(name).proxy_obj
|
|
123
292
|
|
|
124
|
-
def
|
|
125
|
-
|
|
293
|
+
def lookup(self, spec: str) -> ta.Any:
|
|
294
|
+
if '.' not in spec:
|
|
295
|
+
return self._modules_by_name[spec].proxy_obj
|
|
126
296
|
|
|
127
|
-
def get(self, attr: str) -> ta.Any:
|
|
128
297
|
try:
|
|
129
|
-
|
|
298
|
+
module = self._modules_by_name[spec]
|
|
130
299
|
except KeyError:
|
|
131
|
-
|
|
300
|
+
pass
|
|
301
|
+
else:
|
|
302
|
+
return module.proxy_obj
|
|
132
303
|
|
|
133
|
-
|
|
304
|
+
rest, _, attr = spec.rpartition('.')
|
|
305
|
+
module = self._modules_by_name[rest]
|
|
306
|
+
return getattr(module.proxy_obj, attr)
|
|
134
307
|
|
|
135
|
-
if imp.attr is None:
|
|
136
|
-
val = self._import_module(imp.spec)
|
|
137
308
|
|
|
138
|
-
|
|
139
|
-
try:
|
|
140
|
-
mod = self._mods_by_spec[imp.spec]
|
|
141
|
-
except KeyError:
|
|
142
|
-
mod = self._import_module(imp.spec)
|
|
143
|
-
self._mods_by_spec[imp.spec] = mod
|
|
309
|
+
#
|
|
144
310
|
|
|
145
|
-
val = getattr(mod, imp.attr)
|
|
146
311
|
|
|
147
|
-
|
|
312
|
+
_MODULE_PROXY_IMPORTER_GLOBAL_NAME = '__proxy_importer__'
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _get_module_proxy_importer(mod_globals: ta.MutableMapping[str, ta.Any]) -> _ProxyImporter:
|
|
316
|
+
"""Assumed to only be called in a module body - no locking is done."""
|
|
317
|
+
|
|
318
|
+
pi: _ProxyImporter
|
|
319
|
+
try:
|
|
320
|
+
pi = mod_globals[_MODULE_PROXY_IMPORTER_GLOBAL_NAME]
|
|
321
|
+
|
|
322
|
+
except KeyError:
|
|
323
|
+
pi = _ProxyImporter(
|
|
324
|
+
owner_globals=mod_globals,
|
|
325
|
+
)
|
|
326
|
+
mod_globals[_MODULE_PROXY_IMPORTER_GLOBAL_NAME] = pi
|
|
327
|
+
|
|
328
|
+
else:
|
|
329
|
+
if pi.__class__ is not _ProxyImporter:
|
|
330
|
+
raise TypeError(pi)
|
|
331
|
+
|
|
332
|
+
if pi._owner_globals is not mod_globals: # noqa
|
|
333
|
+
raise RuntimeError
|
|
334
|
+
|
|
335
|
+
return pi
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
##
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def proxy_import(
|
|
342
|
+
spec: str,
|
|
343
|
+
package: str | None = None,
|
|
344
|
+
extras: ta.Iterable[str] | None = None,
|
|
345
|
+
*,
|
|
346
|
+
no_cache: bool = False,
|
|
347
|
+
) -> types.ModuleType:
|
|
348
|
+
"""'Legacy' proxy import mechanism."""
|
|
349
|
+
|
|
350
|
+
if isinstance(extras, str):
|
|
351
|
+
raise TypeError(extras)
|
|
352
|
+
|
|
353
|
+
omod = None
|
|
354
|
+
|
|
355
|
+
def __getattr__(att): # noqa
|
|
356
|
+
nonlocal omod
|
|
357
|
+
|
|
358
|
+
if omod is None:
|
|
359
|
+
omod = importlib.import_module(spec, package=package)
|
|
360
|
+
if extras:
|
|
361
|
+
for x in extras:
|
|
362
|
+
importlib.import_module(f'{spec}.{x}', package=package)
|
|
363
|
+
|
|
364
|
+
v = getattr(omod, att)
|
|
365
|
+
|
|
366
|
+
if not no_cache:
|
|
367
|
+
setattr(lmod, att, v)
|
|
368
|
+
|
|
369
|
+
return v
|
|
370
|
+
|
|
371
|
+
lmod = types.ModuleType(spec)
|
|
372
|
+
lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
|
|
373
|
+
return lmod
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
#
|
|
148
377
|
|
|
149
378
|
|
|
150
379
|
def proxy_init(
|
|
@@ -152,6 +381,13 @@ def proxy_init(
|
|
|
152
381
|
spec: str,
|
|
153
382
|
attrs: ta.Iterable[str | tuple[str | None, str | None] | None] | None = None,
|
|
154
383
|
) -> None:
|
|
384
|
+
name = importlib.util.resolve_name(
|
|
385
|
+
spec,
|
|
386
|
+
package=init_globals['__package__'] if spec.startswith('.') else None,
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
#
|
|
390
|
+
|
|
155
391
|
if isinstance(attrs, str):
|
|
156
392
|
raise TypeError(attrs)
|
|
157
393
|
|
|
@@ -181,76 +417,143 @@ def proxy_init(
|
|
|
181
417
|
|
|
182
418
|
#
|
|
183
419
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
420
|
+
pi = _get_module_proxy_importer(init_globals)
|
|
421
|
+
lg = LazyGlobals.install(init_globals)
|
|
422
|
+
|
|
423
|
+
pi.add(
|
|
424
|
+
name,
|
|
425
|
+
children=[r for l, r in al if r is not None],
|
|
187
426
|
)
|
|
188
427
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
pi = init_globals['__proxy_init__']
|
|
428
|
+
for imp_attr, as_attr in al:
|
|
429
|
+
lg.set_fn(as_attr, functools.partial(pi.lookup, name if imp_attr is None else f'{name}.{imp_attr}'))
|
|
192
430
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
431
|
+
|
|
432
|
+
##
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
class _AutoProxy:
|
|
436
|
+
def __init__(
|
|
437
|
+
self,
|
|
438
|
+
mod_globals: ta.MutableMapping[str, ta.Any],
|
|
439
|
+
*,
|
|
440
|
+
disable: bool = False,
|
|
441
|
+
eager: bool = False,
|
|
442
|
+
|
|
443
|
+
unreferenced_callback: ta.Callable[[ta.Sequence[str]], None] | None = None,
|
|
444
|
+
raise_unreferenced: bool = False,
|
|
445
|
+
|
|
446
|
+
update_exports: bool = False,
|
|
447
|
+
|
|
448
|
+
_stack_offset: int = 0,
|
|
449
|
+
_capture_impl: str | None = None,
|
|
450
|
+
) -> None:
|
|
451
|
+
super().__init__()
|
|
452
|
+
|
|
453
|
+
self._mod_globals = mod_globals
|
|
454
|
+
|
|
455
|
+
self._disabled = disable
|
|
456
|
+
self._eager = eager
|
|
457
|
+
|
|
458
|
+
self._unreferenced_callback = unreferenced_callback
|
|
459
|
+
self._raise_unreferenced = raise_unreferenced
|
|
460
|
+
|
|
461
|
+
self._update_exports = update_exports
|
|
462
|
+
|
|
463
|
+
self._ic = ImportCapture(
|
|
464
|
+
mod_globals,
|
|
465
|
+
_hook=_new_import_capture_hook(
|
|
466
|
+
mod_globals,
|
|
467
|
+
stack_offset=_stack_offset + 1,
|
|
468
|
+
capture_impl=_capture_impl,
|
|
469
|
+
),
|
|
470
|
+
disable=disable,
|
|
197
471
|
)
|
|
198
|
-
|
|
472
|
+
self._icc: ta.Any = None
|
|
199
473
|
|
|
200
|
-
|
|
201
|
-
if
|
|
202
|
-
raise
|
|
474
|
+
def __enter__(self) -> ImportCapture:
|
|
475
|
+
if self._icc is not None:
|
|
476
|
+
raise RuntimeError
|
|
203
477
|
|
|
204
|
-
|
|
478
|
+
self._icc = self._ic.capture(
|
|
479
|
+
unreferenced_callback=self._unreferenced_callback,
|
|
480
|
+
raise_unreferenced=self._raise_unreferenced,
|
|
481
|
+
)
|
|
205
482
|
|
|
483
|
+
return self._icc.__enter__() # noqa
|
|
206
484
|
|
|
207
|
-
|
|
485
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
486
|
+
if self._icc is None:
|
|
487
|
+
raise RuntimeError
|
|
208
488
|
|
|
489
|
+
self._icc.__exit__(exc_type, exc_val, exc_tb)
|
|
209
490
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
),
|
|
229
|
-
disable=disable,
|
|
230
|
-
)
|
|
491
|
+
if not self._disabled and exc_type is None:
|
|
492
|
+
self._install()
|
|
493
|
+
|
|
494
|
+
# @abc.abstractmethod
|
|
495
|
+
def _install(self) -> None:
|
|
496
|
+
raise NotImplementedError
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@ta.final
|
|
500
|
+
class _AutoProxyImport(_AutoProxy):
|
|
501
|
+
def _install(self) -> None:
|
|
502
|
+
cap = self._ic.captured
|
|
503
|
+
|
|
504
|
+
for cm in cap.modules.values():
|
|
505
|
+
if cm.attrs:
|
|
506
|
+
raise RuntimeError
|
|
507
|
+
|
|
508
|
+
pi = _get_module_proxy_importer(self._mod_globals)
|
|
231
509
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
raise_unreferenced=raise_unreferenced,
|
|
237
|
-
):
|
|
238
|
-
yield inst
|
|
239
|
-
|
|
240
|
-
for pi in inst.captured.imports:
|
|
241
|
-
proxy_init(
|
|
242
|
-
init_globals,
|
|
243
|
-
pi.spec,
|
|
244
|
-
pi.attrs,
|
|
510
|
+
for cm in cap.modules.values():
|
|
511
|
+
pi.add(
|
|
512
|
+
cm.name,
|
|
513
|
+
children=cm.children,
|
|
245
514
|
)
|
|
246
515
|
|
|
247
|
-
|
|
248
|
-
|
|
516
|
+
for ci in cap.imports.values():
|
|
517
|
+
pm = pi.get_module(ci.module.name)
|
|
518
|
+
for a in ci.as_ or ():
|
|
519
|
+
self._mod_globals[a] = pm
|
|
249
520
|
|
|
250
|
-
|
|
521
|
+
if self._eager:
|
|
522
|
+
for ci in cap.imports.values():
|
|
523
|
+
pi.lookup(ci.module.name)
|
|
524
|
+
|
|
525
|
+
if self._update_exports:
|
|
526
|
+
self._ic.update_exports()
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
@ta.final
|
|
530
|
+
class _AutoProxyInit(_AutoProxy):
|
|
531
|
+
def _install(self) -> None:
|
|
532
|
+
cap = self._ic.captured
|
|
533
|
+
|
|
534
|
+
pi = _get_module_proxy_importer(self._mod_globals)
|
|
535
|
+
lg = LazyGlobals.install(self._mod_globals)
|
|
536
|
+
|
|
537
|
+
for cm in cap.modules.values():
|
|
538
|
+
pi.add(
|
|
539
|
+
cm.name,
|
|
540
|
+
children=cm.children,
|
|
541
|
+
attrs=cm.attrs,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
for ci in cap.imports.values():
|
|
545
|
+
for a in ci.as_ or ():
|
|
546
|
+
lg.set_fn(a, functools.partial(pi.lookup, ci.module.name))
|
|
547
|
+
for sa, da in ci.attrs or ():
|
|
548
|
+
lg.set_fn(da, functools.partial(pi.lookup, f'{ci.module.name}.{sa}'))
|
|
549
|
+
|
|
550
|
+
if self._eager:
|
|
551
|
+
for a in cap.attrs:
|
|
251
552
|
lg.get(a)
|
|
252
553
|
|
|
253
|
-
if
|
|
254
|
-
|
|
554
|
+
if self._update_exports:
|
|
555
|
+
self._ic.update_exports()
|
|
556
|
+
|
|
255
557
|
|
|
256
|
-
|
|
558
|
+
auto_proxy_import = _AutoProxyImport
|
|
559
|
+
auto_proxy_init = _AutoProxyInit
|