omlish 0.0.0.dev397__py3-none-any.whl → 0.0.0.dev399__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.
- omlish/__about__.py +2 -2
- omlish/bootstrap/__init__.py +2 -2
- omlish/lang/__init__.py +26 -5
- omlish/lang/imports/__init__.py +0 -0
- omlish/lang/imports/conditional.py +33 -0
- omlish/lang/imports/lazy.py +66 -0
- omlish/lang/imports/proxyinit.py +441 -0
- omlish/lang/imports/resolution.py +86 -0
- omlish/lang/imports/traversal.py +94 -0
- omlish/lang/lazyglobals.py +59 -0
- omlish/lang/resources.py +1 -1
- omlish/marshal/__init__.py +2 -2
- omlish/secrets/all.py +2 -2
- omlish/secrets/secrets.py +1 -1
- omlish/specs/jsonrpc/__init__.py +2 -2
- omlish/specs/jsonschema/__init__.py +2 -2
- omlish/specs/openapi/__init__.py +2 -2
- omlish/sql/api/__init__.py +2 -2
- omlish/sql/queries/__init__.py +3 -9
- omlish/sql/tabledefs/__init__.py +2 -2
- omlish/typedvalues/__init__.py +2 -2
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/METADATA +1 -1
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/RECORD +27 -21
- omlish/lang/imports.py +0 -418
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev397.dist-info → omlish-0.0.0.dev399.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
omlish/bootstrap/__init__.py
CHANGED
@@ -37,6 +37,6 @@ from .sys import ( # noqa
|
|
37
37
|
##
|
38
38
|
|
39
39
|
|
40
|
-
from ..
|
40
|
+
from .. import lang as _lang
|
41
41
|
|
42
|
-
|
42
|
+
_lang.register_conditional_import('..marshal', '.marshal', __package__)
|
omlish/lang/__init__.py
CHANGED
@@ -219,16 +219,33 @@ from .generators import ( # noqa
|
|
219
219
|
nextgen,
|
220
220
|
)
|
221
221
|
|
222
|
-
from .imports import ( # noqa
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
222
|
+
from .imports.conditional import ( # noqa
|
223
|
+
register_conditional_import,
|
224
|
+
trigger_conditional_imports,
|
225
|
+
)
|
226
|
+
|
227
|
+
from .imports.lazy import ( # noqa
|
227
228
|
lazy_import,
|
228
229
|
proxy_import,
|
230
|
+
)
|
231
|
+
|
232
|
+
from .imports.proxyinit import ( # noqa
|
229
233
|
proxy_init,
|
234
|
+
|
235
|
+
AutoProxyInitError,
|
236
|
+
AutoProxyInitErrors,
|
237
|
+
auto_proxy_init,
|
238
|
+
)
|
239
|
+
|
240
|
+
from .imports.resolution import ( # noqa
|
241
|
+
can_import,
|
242
|
+
get_real_module_name,
|
230
243
|
resolve_import_name,
|
231
244
|
try_import,
|
245
|
+
)
|
246
|
+
|
247
|
+
from .imports.traversal import ( # noqa
|
248
|
+
import_all,
|
232
249
|
yield_import_all,
|
233
250
|
yield_importable,
|
234
251
|
)
|
@@ -251,6 +268,10 @@ from .iterables import ( # noqa
|
|
251
268
|
take,
|
252
269
|
)
|
253
270
|
|
271
|
+
from .lazyglobals import ( # noqa
|
272
|
+
LazyGlobals,
|
273
|
+
)
|
274
|
+
|
254
275
|
from .maysyncs import ( # noqa
|
255
276
|
MaysyncP,
|
256
277
|
|
File without changes
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import sys
|
2
|
+
|
3
|
+
from .resolution import resolve_import_name
|
4
|
+
|
5
|
+
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
# dict[str, None] to preserve insertion order - we don't have OrderedSet here
|
10
|
+
_REGISTERED_CONDITIONAL_IMPORTS: dict[str, dict[str, None] | None] = {}
|
11
|
+
|
12
|
+
|
13
|
+
def register_conditional_import(when: str, then: str, package: str | None = None) -> None:
|
14
|
+
wn = resolve_import_name(when, package)
|
15
|
+
tn = resolve_import_name(then, package)
|
16
|
+
if tn in sys.modules:
|
17
|
+
return
|
18
|
+
if wn in sys.modules:
|
19
|
+
__import__(tn)
|
20
|
+
else:
|
21
|
+
tns = _REGISTERED_CONDITIONAL_IMPORTS.setdefault(wn, {})
|
22
|
+
if tns is None:
|
23
|
+
raise Exception(f'Conditional import trigger already cleared: {wn=} {tn=}')
|
24
|
+
tns[tn] = None
|
25
|
+
|
26
|
+
|
27
|
+
def trigger_conditional_imports(package: str) -> None:
|
28
|
+
tns = _REGISTERED_CONDITIONAL_IMPORTS.get(package, {})
|
29
|
+
if tns is None:
|
30
|
+
raise Exception(f'Conditional import trigger already cleared: {package=}')
|
31
|
+
_REGISTERED_CONDITIONAL_IMPORTS[package] = None
|
32
|
+
for tn in tns:
|
33
|
+
__import__(tn)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import importlib.util
|
2
|
+
import types
|
3
|
+
import typing as ta
|
4
|
+
|
5
|
+
|
6
|
+
##
|
7
|
+
|
8
|
+
|
9
|
+
def lazy_import(
|
10
|
+
name: str,
|
11
|
+
package: str | None = None,
|
12
|
+
*,
|
13
|
+
optional: bool = False,
|
14
|
+
cache_failure: bool = False,
|
15
|
+
) -> ta.Callable[[], ta.Any]:
|
16
|
+
result = not_set = object()
|
17
|
+
|
18
|
+
def inner():
|
19
|
+
nonlocal result
|
20
|
+
|
21
|
+
if result is not not_set:
|
22
|
+
if isinstance(result, Exception):
|
23
|
+
raise result
|
24
|
+
return result
|
25
|
+
|
26
|
+
try:
|
27
|
+
mod = importlib.import_module(name, package=package)
|
28
|
+
|
29
|
+
except Exception as e:
|
30
|
+
if optional:
|
31
|
+
if cache_failure:
|
32
|
+
result = None
|
33
|
+
return None
|
34
|
+
|
35
|
+
if cache_failure:
|
36
|
+
result = e
|
37
|
+
raise
|
38
|
+
|
39
|
+
result = mod
|
40
|
+
return mod
|
41
|
+
|
42
|
+
return inner
|
43
|
+
|
44
|
+
|
45
|
+
def proxy_import(
|
46
|
+
name: str,
|
47
|
+
package: str | None = None,
|
48
|
+
extras: ta.Iterable[str] | None = None,
|
49
|
+
) -> types.ModuleType:
|
50
|
+
if isinstance(extras, str):
|
51
|
+
raise TypeError(extras)
|
52
|
+
|
53
|
+
omod = None
|
54
|
+
|
55
|
+
def __getattr__(att): # noqa
|
56
|
+
nonlocal omod
|
57
|
+
if omod is None:
|
58
|
+
omod = importlib.import_module(name, package=package)
|
59
|
+
if extras:
|
60
|
+
for x in extras:
|
61
|
+
importlib.import_module(f'{name}.{x}', package=package)
|
62
|
+
return getattr(omod, att)
|
63
|
+
|
64
|
+
lmod = types.ModuleType(name)
|
65
|
+
lmod.__getattr__ = __getattr__ # type: ignore
|
66
|
+
return lmod
|
@@ -0,0 +1,441 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- auto_proxy_init can capture `import as` by scanning globals for sentinels
|
4
|
+
- replaces _AutoProxyInitCapture._attrs dict outright
|
5
|
+
- should raise on unbound or shadowed import - was probably imported for side-effects but will never get
|
6
|
+
proxy imported
|
7
|
+
"""
|
8
|
+
import builtins
|
9
|
+
import contextlib
|
10
|
+
import functools
|
11
|
+
import importlib.util
|
12
|
+
import types
|
13
|
+
import typing as ta
|
14
|
+
|
15
|
+
from ..lazyglobals import LazyGlobals
|
16
|
+
|
17
|
+
|
18
|
+
##
|
19
|
+
|
20
|
+
|
21
|
+
class NamePackage(ta.NamedTuple):
|
22
|
+
name: str
|
23
|
+
package: str
|
24
|
+
|
25
|
+
|
26
|
+
class _ProxyInit:
|
27
|
+
class _Import(ta.NamedTuple):
|
28
|
+
pkg: str
|
29
|
+
attr: str | None
|
30
|
+
|
31
|
+
def __init__(
|
32
|
+
self,
|
33
|
+
lazy_globals: LazyGlobals,
|
34
|
+
name_package: NamePackage,
|
35
|
+
) -> None:
|
36
|
+
super().__init__()
|
37
|
+
|
38
|
+
self._lazy_globals = lazy_globals
|
39
|
+
self._name_package = name_package
|
40
|
+
|
41
|
+
self._imps_by_attr: dict[str, _ProxyInit._Import] = {}
|
42
|
+
self._mods_by_pkgs: dict[str, ta.Any] = {}
|
43
|
+
|
44
|
+
@property
|
45
|
+
def name_package(self) -> NamePackage:
|
46
|
+
return self._name_package
|
47
|
+
|
48
|
+
def add(
|
49
|
+
self,
|
50
|
+
package: str,
|
51
|
+
attrs: ta.Iterable[str | tuple[str, str]] | None = None,
|
52
|
+
) -> None:
|
53
|
+
if isinstance(attrs, str):
|
54
|
+
raise TypeError(attrs)
|
55
|
+
|
56
|
+
if attrs is None:
|
57
|
+
whole_attr = package.split('.')[-1]
|
58
|
+
|
59
|
+
self._imps_by_attr[whole_attr] = self._Import(package, None)
|
60
|
+
self._lazy_globals.set_fn(whole_attr, functools.partial(self.get, whole_attr))
|
61
|
+
|
62
|
+
else:
|
63
|
+
for attr in attrs:
|
64
|
+
if isinstance(attr, tuple):
|
65
|
+
imp_attr, attr = attr
|
66
|
+
else:
|
67
|
+
imp_attr = attr
|
68
|
+
|
69
|
+
self._imps_by_attr[attr] = self._Import(package, imp_attr)
|
70
|
+
self._lazy_globals.set_fn(attr, functools.partial(self.get, attr))
|
71
|
+
|
72
|
+
def get(self, attr: str) -> ta.Any:
|
73
|
+
try:
|
74
|
+
imp = self._imps_by_attr[attr]
|
75
|
+
except KeyError:
|
76
|
+
raise AttributeError(attr) # noqa
|
77
|
+
|
78
|
+
val: ta.Any
|
79
|
+
|
80
|
+
if imp.attr is None:
|
81
|
+
val = importlib.import_module(imp.pkg, package=self._name_package.package)
|
82
|
+
|
83
|
+
else:
|
84
|
+
try:
|
85
|
+
mod = self._mods_by_pkgs[imp.pkg]
|
86
|
+
except KeyError:
|
87
|
+
mod = importlib.import_module(imp.pkg, package=self._name_package.package)
|
88
|
+
|
89
|
+
val = getattr(mod, imp.attr)
|
90
|
+
|
91
|
+
return val
|
92
|
+
|
93
|
+
|
94
|
+
def proxy_init(
|
95
|
+
init_globals: ta.MutableMapping[str, ta.Any],
|
96
|
+
package: str,
|
97
|
+
attrs: ta.Iterable[str | tuple[str, str]] | None = None,
|
98
|
+
) -> None:
|
99
|
+
if isinstance(attrs, str):
|
100
|
+
raise TypeError(attrs)
|
101
|
+
|
102
|
+
init_name_package = NamePackage(
|
103
|
+
init_globals['__name__'],
|
104
|
+
init_globals['__package__'],
|
105
|
+
)
|
106
|
+
|
107
|
+
pi: _ProxyInit
|
108
|
+
try:
|
109
|
+
pi = init_globals['__proxy_init__']
|
110
|
+
|
111
|
+
except KeyError:
|
112
|
+
pi = _ProxyInit(
|
113
|
+
LazyGlobals.install(init_globals),
|
114
|
+
init_name_package,
|
115
|
+
)
|
116
|
+
init_globals['__proxy_init__'] = pi
|
117
|
+
|
118
|
+
else:
|
119
|
+
if pi.name_package != init_name_package:
|
120
|
+
raise Exception(f'Wrong init name: {pi.name_package=} != {init_name_package=}')
|
121
|
+
|
122
|
+
pi.add(package, attrs)
|
123
|
+
|
124
|
+
|
125
|
+
##
|
126
|
+
|
127
|
+
|
128
|
+
class AutoProxyInitError(Exception):
|
129
|
+
pass
|
130
|
+
|
131
|
+
|
132
|
+
class AutoProxyInitErrors:
|
133
|
+
def __new__(cls, *args, **kwargs): # noqa
|
134
|
+
raise TypeError
|
135
|
+
|
136
|
+
class HookError(AutoProxyInitError):
|
137
|
+
pass
|
138
|
+
|
139
|
+
class AttrError(AutoProxyInitError):
|
140
|
+
def __init__(self, module: str | None, name: str) -> None:
|
141
|
+
super().__init__()
|
142
|
+
|
143
|
+
self.module = module
|
144
|
+
self.name = name
|
145
|
+
|
146
|
+
def __repr__(self) -> str:
|
147
|
+
return f'{self.__class__.__qualname__}(module={self.module!r}, name={self.name!r})'
|
148
|
+
|
149
|
+
class ImportError(AutoProxyInitError): # noqa
|
150
|
+
def __init__(self, module: str, from_list: ta.Sequence[str] | None) -> None:
|
151
|
+
super().__init__()
|
152
|
+
|
153
|
+
self.module = module
|
154
|
+
self.from_list = from_list
|
155
|
+
|
156
|
+
def __repr__(self) -> str:
|
157
|
+
return f'{self.__class__.__qualname__}(module={self.module!r}, from_list={self.from_list!r})'
|
158
|
+
|
159
|
+
class ImportStarForbiddenError(ImportError):
|
160
|
+
pass
|
161
|
+
|
162
|
+
class UnproxiedImportForbiddenError(ImportError):
|
163
|
+
pass
|
164
|
+
|
165
|
+
|
166
|
+
class _AutoProxyInitCapture:
|
167
|
+
class ModuleSpec(ta.NamedTuple):
|
168
|
+
name: str
|
169
|
+
level: int
|
170
|
+
|
171
|
+
def __str__(self) -> str:
|
172
|
+
return f'{"." * self.level}{self.name}'
|
173
|
+
|
174
|
+
def __repr__(self) -> str:
|
175
|
+
return repr(str(self))
|
176
|
+
|
177
|
+
class _ModuleAttr:
|
178
|
+
def __init__(self, module: '_AutoProxyInitCapture._Module', name: str) -> None:
|
179
|
+
super().__init__()
|
180
|
+
|
181
|
+
self.__module = module
|
182
|
+
self.__name = name
|
183
|
+
|
184
|
+
def __repr__(self) -> str:
|
185
|
+
return f'<{self.__class__.__name__}: {f"{self.__module.spec}:{self.__name}"!r}>'
|
186
|
+
|
187
|
+
class _Module:
|
188
|
+
def __init__(self, spec: '_AutoProxyInitCapture.ModuleSpec') -> None:
|
189
|
+
super().__init__()
|
190
|
+
|
191
|
+
self.spec = spec
|
192
|
+
|
193
|
+
self.module = types.ModuleType(f'<{self.__class__.__qualname__}: {spec!r}>')
|
194
|
+
|
195
|
+
self.attrs: dict[str, _AutoProxyInitCapture._ModuleAttr] = {}
|
196
|
+
self.imported_whole = False
|
197
|
+
|
198
|
+
def __repr__(self) -> str:
|
199
|
+
return f'{self.__class__.__name__}({self.spec!r})'
|
200
|
+
|
201
|
+
def __init__(self) -> None:
|
202
|
+
super().__init__()
|
203
|
+
|
204
|
+
self._modules: dict[_AutoProxyInitCapture.ModuleSpec, _AutoProxyInitCapture._Module] = {}
|
205
|
+
self._attrs: dict[str, _AutoProxyInitCapture._ModuleAttr | _AutoProxyInitCapture._Module] = {}
|
206
|
+
|
207
|
+
def _handle_import(
|
208
|
+
self,
|
209
|
+
module: _Module,
|
210
|
+
*,
|
211
|
+
from_list: ta.Sequence[str] | None,
|
212
|
+
) -> None:
|
213
|
+
if from_list is None:
|
214
|
+
if module.spec.level or not module.spec.name:
|
215
|
+
raise AutoProxyInitError
|
216
|
+
|
217
|
+
attr = module.spec.name
|
218
|
+
|
219
|
+
try:
|
220
|
+
xma: ta.Any = self._attrs[attr]
|
221
|
+
except KeyError:
|
222
|
+
pass
|
223
|
+
|
224
|
+
else:
|
225
|
+
if (
|
226
|
+
xma is not self._attrs.get(attr) or
|
227
|
+
not module.imported_whole
|
228
|
+
):
|
229
|
+
raise AutoProxyInitErrors.AttrError(str(module.spec), attr)
|
230
|
+
|
231
|
+
return
|
232
|
+
|
233
|
+
self._attrs[attr] = module
|
234
|
+
module.imported_whole = True
|
235
|
+
|
236
|
+
else:
|
237
|
+
for attr in from_list:
|
238
|
+
if attr == '*':
|
239
|
+
raise AutoProxyInitErrors.ImportStarForbiddenError(str(module.spec), from_list)
|
240
|
+
|
241
|
+
try:
|
242
|
+
xma = getattr(module.module, attr)
|
243
|
+
except AttributeError:
|
244
|
+
pass
|
245
|
+
|
246
|
+
else:
|
247
|
+
if (
|
248
|
+
xma is not module.attrs.get(attr) or
|
249
|
+
xma is not self._attrs.get(attr)
|
250
|
+
):
|
251
|
+
raise AutoProxyInitErrors.AttrError(str(module.spec), attr)
|
252
|
+
|
253
|
+
continue
|
254
|
+
|
255
|
+
if attr in self._attrs:
|
256
|
+
raise AutoProxyInitErrors.AttrError(str(module.spec), attr)
|
257
|
+
|
258
|
+
ma = _AutoProxyInitCapture._ModuleAttr(module, attr)
|
259
|
+
self._attrs[attr] = ma
|
260
|
+
module.attrs[attr] = ma
|
261
|
+
setattr(module.module, attr, ma)
|
262
|
+
|
263
|
+
_MOD_SELF_ATTR: ta.ClassVar[str] = '__auto_proxy_init_capture__'
|
264
|
+
|
265
|
+
def _intercept_import(
|
266
|
+
self,
|
267
|
+
name: str,
|
268
|
+
*,
|
269
|
+
globals: ta.Mapping[str, ta.Any] | None = None, # noqa
|
270
|
+
from_list: ta.Sequence[str] | None = None,
|
271
|
+
level: int = 0,
|
272
|
+
) -> types.ModuleType | None:
|
273
|
+
if not (
|
274
|
+
globals is not None and
|
275
|
+
globals.get(self._MOD_SELF_ATTR) is self
|
276
|
+
):
|
277
|
+
return None
|
278
|
+
|
279
|
+
spec = _AutoProxyInitCapture.ModuleSpec(name, level)
|
280
|
+
try:
|
281
|
+
module = self._modules[spec]
|
282
|
+
except KeyError:
|
283
|
+
module = self._Module(spec)
|
284
|
+
self._modules[spec] = module
|
285
|
+
|
286
|
+
self._handle_import(
|
287
|
+
module,
|
288
|
+
from_list=from_list,
|
289
|
+
)
|
290
|
+
|
291
|
+
return module.module
|
292
|
+
|
293
|
+
@contextlib.contextmanager
|
294
|
+
def hook_context(
|
295
|
+
self,
|
296
|
+
init_globals: ta.MutableMapping[str, ta.Any], # noqa
|
297
|
+
*,
|
298
|
+
forbid_unproxied_imports: bool = False,
|
299
|
+
) -> ta.Iterator[None]:
|
300
|
+
if self._MOD_SELF_ATTR in init_globals:
|
301
|
+
raise AutoProxyInitErrors.HookError
|
302
|
+
|
303
|
+
old_import = builtins.__import__
|
304
|
+
|
305
|
+
def new_import(
|
306
|
+
name,
|
307
|
+
globals=None, # noqa
|
308
|
+
locals=None, # noqa
|
309
|
+
fromlist=None,
|
310
|
+
level=0,
|
311
|
+
):
|
312
|
+
if (im := self._intercept_import(
|
313
|
+
name,
|
314
|
+
globals=globals,
|
315
|
+
from_list=fromlist,
|
316
|
+
level=level,
|
317
|
+
)) is not None:
|
318
|
+
return im
|
319
|
+
|
320
|
+
if forbid_unproxied_imports:
|
321
|
+
raise AutoProxyInitErrors.UnproxiedImportForbiddenError(
|
322
|
+
str(_AutoProxyInitCapture.ModuleSpec(name, level)),
|
323
|
+
fromlist,
|
324
|
+
)
|
325
|
+
|
326
|
+
return old_import(
|
327
|
+
name,
|
328
|
+
globals=globals,
|
329
|
+
locals=locals,
|
330
|
+
fromlist=fromlist,
|
331
|
+
level=level,
|
332
|
+
)
|
333
|
+
|
334
|
+
#
|
335
|
+
|
336
|
+
init_globals[self._MOD_SELF_ATTR] = self
|
337
|
+
builtins.__import__ = new_import
|
338
|
+
|
339
|
+
try:
|
340
|
+
yield
|
341
|
+
|
342
|
+
finally:
|
343
|
+
if not (
|
344
|
+
init_globals[self._MOD_SELF_ATTR] is self and
|
345
|
+
builtins.__import__ is new_import
|
346
|
+
):
|
347
|
+
raise AutoProxyInitErrors.HookError
|
348
|
+
|
349
|
+
del init_globals[self._MOD_SELF_ATTR]
|
350
|
+
builtins.__import__ = old_import
|
351
|
+
|
352
|
+
def verify_globals(
|
353
|
+
self,
|
354
|
+
init_globals: ta.MutableMapping[str, ta.Any], # noqa
|
355
|
+
) -> None:
|
356
|
+
for attr, obj in self._attrs.items():
|
357
|
+
try:
|
358
|
+
xo = init_globals[attr]
|
359
|
+
except KeyError:
|
360
|
+
raise AutoProxyInitErrors.AttrError(None, attr) from None
|
361
|
+
|
362
|
+
if isinstance(obj, _AutoProxyInitCapture._ModuleAttr):
|
363
|
+
if xo is not obj:
|
364
|
+
raise AutoProxyInitErrors.AttrError(None, attr) from None
|
365
|
+
|
366
|
+
elif isinstance(obj, _AutoProxyInitCapture._Module):
|
367
|
+
if xo is not obj.module:
|
368
|
+
raise AutoProxyInitErrors.AttrError(None, attr) from None
|
369
|
+
|
370
|
+
else:
|
371
|
+
raise TypeError(obj)
|
372
|
+
|
373
|
+
@property
|
374
|
+
def all_attrs(self) -> ta.AbstractSet[str]:
|
375
|
+
return self._attrs.keys()
|
376
|
+
|
377
|
+
class ProxyInit(ta.NamedTuple):
|
378
|
+
package: str
|
379
|
+
attrs: ta.Sequence[str] | None
|
380
|
+
|
381
|
+
def build_proxy_inits(self) -> list[ProxyInit]:
|
382
|
+
lst: list[_AutoProxyInitCapture.ProxyInit] = []
|
383
|
+
|
384
|
+
for module in self._modules.values():
|
385
|
+
if module.imported_whole:
|
386
|
+
lst.append(_AutoProxyInitCapture.ProxyInit(str(module.spec), None))
|
387
|
+
|
388
|
+
if module.attrs:
|
389
|
+
if not module.spec.name:
|
390
|
+
for attr in module.attrs:
|
391
|
+
if not module.spec.level:
|
392
|
+
raise AutoProxyInitError
|
393
|
+
|
394
|
+
lst.append(_AutoProxyInitCapture.ProxyInit('.' * module.spec.level + attr, None))
|
395
|
+
|
396
|
+
else:
|
397
|
+
lst.append(_AutoProxyInitCapture.ProxyInit(str(module.spec), list(module.attrs)))
|
398
|
+
|
399
|
+
return lst
|
400
|
+
|
401
|
+
|
402
|
+
@contextlib.contextmanager
|
403
|
+
def auto_proxy_init(
|
404
|
+
init_globals: ta.MutableMapping[str, ta.Any],
|
405
|
+
*,
|
406
|
+
disable: bool = False,
|
407
|
+
eager: bool = False,
|
408
|
+
) -> ta.Iterator[None]:
|
409
|
+
"""
|
410
|
+
This is a bit extreme - use sparingly. It relies on an interpreter-global import lock, but much of the ecosystem
|
411
|
+
implicitly does anyway. It further relies on temporarily patching `__builtins__.__import__`, but could be switched
|
412
|
+
to use any number of other import hooks.
|
413
|
+
"""
|
414
|
+
|
415
|
+
if disable:
|
416
|
+
yield
|
417
|
+
return
|
418
|
+
|
419
|
+
cap = _AutoProxyInitCapture()
|
420
|
+
|
421
|
+
with cap.hook_context(init_globals):
|
422
|
+
yield
|
423
|
+
|
424
|
+
cap.verify_globals(init_globals)
|
425
|
+
|
426
|
+
pis = cap.build_proxy_inits()
|
427
|
+
|
428
|
+
for attr in cap.all_attrs:
|
429
|
+
del init_globals[attr]
|
430
|
+
|
431
|
+
for pi in pis:
|
432
|
+
proxy_init(
|
433
|
+
init_globals,
|
434
|
+
pi.package,
|
435
|
+
pi.attrs,
|
436
|
+
)
|
437
|
+
|
438
|
+
if eager:
|
439
|
+
lg = LazyGlobals.install(init_globals)
|
440
|
+
for attr in cap.all_attrs:
|
441
|
+
lg.get(attr)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
"""
|
2
|
+
TODO:
|
3
|
+
- use importlib.util.resolve_name
|
4
|
+
"""
|
5
|
+
import importlib.util
|
6
|
+
import sys
|
7
|
+
import types
|
8
|
+
import typing as ta
|
9
|
+
|
10
|
+
|
11
|
+
##
|
12
|
+
|
13
|
+
|
14
|
+
def can_import(name: str, package: str | None = None) -> bool:
|
15
|
+
try:
|
16
|
+
spec = importlib.util.find_spec(name, package)
|
17
|
+
except ImportError:
|
18
|
+
return False
|
19
|
+
else:
|
20
|
+
return spec is not None
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
|
25
|
+
|
26
|
+
def try_import(spec: str) -> types.ModuleType | None:
|
27
|
+
s = spec.lstrip('.')
|
28
|
+
l = len(spec) - len(s)
|
29
|
+
try:
|
30
|
+
return __import__(s, globals(), level=l)
|
31
|
+
except ImportError:
|
32
|
+
return None
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
|
37
|
+
|
38
|
+
def resolve_import_name(name: str, package: str | None = None) -> str:
|
39
|
+
# FIXME: importlib.util.resolve_name
|
40
|
+
level = 0
|
41
|
+
|
42
|
+
if name.startswith('.'):
|
43
|
+
if not package:
|
44
|
+
raise TypeError("the 'package' argument is required to perform a relative import for {name!r}")
|
45
|
+
for character in name:
|
46
|
+
if character != '.':
|
47
|
+
break
|
48
|
+
level += 1
|
49
|
+
|
50
|
+
name = name[level:]
|
51
|
+
|
52
|
+
if not isinstance(name, str):
|
53
|
+
raise TypeError(f'module name must be str, not {type(name)}')
|
54
|
+
if level < 0:
|
55
|
+
raise ValueError('level must be >= 0')
|
56
|
+
if level > 0:
|
57
|
+
if not isinstance(package, str):
|
58
|
+
raise TypeError('__package__ not set to a string')
|
59
|
+
elif not package:
|
60
|
+
raise ImportError('attempted relative import with no known parent package')
|
61
|
+
if not name and level == 0:
|
62
|
+
raise ValueError('Empty module name')
|
63
|
+
|
64
|
+
if level > 0:
|
65
|
+
bits = package.rsplit('.', level - 1) # type: ignore
|
66
|
+
if len(bits) < level:
|
67
|
+
raise ImportError('attempted relative import beyond top-level package')
|
68
|
+
base = bits[0]
|
69
|
+
name = f'{base}.{name}' if name else base
|
70
|
+
|
71
|
+
return name
|
72
|
+
|
73
|
+
|
74
|
+
##
|
75
|
+
|
76
|
+
|
77
|
+
def get_real_module_name(globals: ta.Mapping[str, ta.Any]) -> str: # noqa
|
78
|
+
module = sys.modules[globals['__name__']]
|
79
|
+
|
80
|
+
if module.__spec__ and module.__spec__.name:
|
81
|
+
return module.__spec__.name
|
82
|
+
|
83
|
+
if module.__package__:
|
84
|
+
return module.__package__
|
85
|
+
|
86
|
+
raise RuntimeError("Can't determine real module name")
|