omlish 0.0.0.dev432__py3-none-any.whl → 0.0.0.dev434__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 +3 -3
- omlish/asyncs/all.py +0 -13
- omlish/inject/__init__.py +152 -126
- omlish/inject/binder.py +7 -4
- omlish/inject/impl/elements.py +6 -8
- omlish/inject/impl/injector.py +58 -32
- omlish/inject/impl/inspect.py +1 -0
- omlish/inject/impl/maysync.py +44 -0
- omlish/inject/impl/multis.py +5 -5
- omlish/inject/impl/privates.py +8 -8
- omlish/inject/impl/providers.py +24 -31
- omlish/inject/impl/providers2.py +43 -0
- omlish/inject/impl/scopes.py +19 -25
- omlish/inject/impl/sync.py +42 -0
- omlish/inject/injector.py +9 -11
- omlish/inject/inspect.py +1 -3
- omlish/inject/listeners.py +4 -4
- omlish/inject/managed.py +52 -20
- omlish/inject/maysync.py +27 -0
- omlish/inject/providers.py +6 -0
- omlish/inject/scopes.py +38 -10
- omlish/inject/sync.py +46 -0
- omlish/lang/__init__.py +23 -6
- omlish/lang/asyncs.py +18 -0
- omlish/lang/contextmanagers.py +23 -0
- omlish/lang/imports/capture.py +491 -0
- omlish/lang/imports/lazy.py +0 -25
- omlish/lang/imports/proxy.py +62 -0
- omlish/lang/imports/proxyinit.py +28 -518
- omlish/lang/resources.py +1 -1
- omlish/lite/asyncs.py +21 -0
- omlish/lite/maysync.py +21 -0
- omlish/logs/contexts.py +4 -3
- omlish/logs/std/records.py +29 -19
- omlish/logs/typed/bindings.py +84 -37
- omlish/logs/typed/types.py +16 -1
- omlish/marshal/__init__.py +1 -1
- omlish/typedvalues/__init__.py +1 -1
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/METADATA +3 -6
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/RECORD +44 -39
- omlish/asyncs/bridge.py +0 -359
- omlish/asyncs/utils.py +0 -18
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev432.dist-info → omlish-0.0.0.dev434.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,491 @@
|
|
1
|
+
import builtins
|
2
|
+
import contextlib
|
3
|
+
import functools
|
4
|
+
import types
|
5
|
+
import typing as ta
|
6
|
+
|
7
|
+
|
8
|
+
##
|
9
|
+
|
10
|
+
|
11
|
+
class ImportCaptureError(Exception):
|
12
|
+
pass
|
13
|
+
|
14
|
+
|
15
|
+
class ImportCaptureErrors:
|
16
|
+
def __new__(cls, *args, **kwargs): # noqa
|
17
|
+
raise TypeError
|
18
|
+
|
19
|
+
class HookError(ImportCaptureError):
|
20
|
+
pass
|
21
|
+
|
22
|
+
class AttrError(ImportCaptureError):
|
23
|
+
def __init__(self, module: str | None, name: str) -> None:
|
24
|
+
super().__init__()
|
25
|
+
|
26
|
+
self.module = module
|
27
|
+
self.name = name
|
28
|
+
|
29
|
+
def __repr__(self) -> str:
|
30
|
+
return f'{self.__class__.__qualname__}(module={self.module!r}, name={self.name!r})'
|
31
|
+
|
32
|
+
class ImportError(ImportCaptureError): # noqa
|
33
|
+
def __init__(self, module: str, from_list: ta.Sequence[str] | None) -> None:
|
34
|
+
super().__init__()
|
35
|
+
|
36
|
+
self.module = module
|
37
|
+
self.from_list = from_list
|
38
|
+
|
39
|
+
def __repr__(self) -> str:
|
40
|
+
return f'{self.__class__.__qualname__}(module={self.module!r}, from_list={self.from_list!r})'
|
41
|
+
|
42
|
+
class ImportStarForbiddenError(ImportError):
|
43
|
+
pass
|
44
|
+
|
45
|
+
class UncapturedImportForbiddenError(ImportError):
|
46
|
+
pass
|
47
|
+
|
48
|
+
class UnreferencedImportsError(ImportCaptureError):
|
49
|
+
def __init__(self, unreferenced: ta.Mapping[str, ta.Sequence[str | None]]) -> None:
|
50
|
+
super().__init__()
|
51
|
+
|
52
|
+
self.unreferenced = unreferenced
|
53
|
+
|
54
|
+
def __repr__(self) -> str:
|
55
|
+
return f'{self.__class__.__qualname__}(unreferenced={self.unreferenced!r})'
|
56
|
+
|
57
|
+
class CaptureInProgressError(ImportCaptureError):
|
58
|
+
pass
|
59
|
+
|
60
|
+
|
61
|
+
class _ImportCaptureImpl:
|
62
|
+
class ModuleSpec(ta.NamedTuple):
|
63
|
+
name: str
|
64
|
+
level: int
|
65
|
+
|
66
|
+
def __str__(self) -> str:
|
67
|
+
return f'{"." * self.level}{self.name}'
|
68
|
+
|
69
|
+
def __repr__(self) -> str:
|
70
|
+
return repr(str(self))
|
71
|
+
|
72
|
+
def __init__(self) -> None:
|
73
|
+
super().__init__()
|
74
|
+
|
75
|
+
self._modules_by_spec: dict[_ImportCaptureImpl.ModuleSpec, _ImportCaptureImpl._Module] = {}
|
76
|
+
self._modules_by_module_obj: dict[types.ModuleType, _ImportCaptureImpl._Module] = {}
|
77
|
+
|
78
|
+
self._attrs: dict[_ImportCaptureImpl._ModuleAttr, tuple[_ImportCaptureImpl._Module, str]] = {}
|
79
|
+
|
80
|
+
#
|
81
|
+
|
82
|
+
class _ModuleAttr:
|
83
|
+
def __init__(
|
84
|
+
self,
|
85
|
+
module: '_ImportCaptureImpl._Module',
|
86
|
+
name: str,
|
87
|
+
) -> None:
|
88
|
+
super().__init__()
|
89
|
+
|
90
|
+
self.__module = module
|
91
|
+
self.__name = name
|
92
|
+
|
93
|
+
def __repr__(self) -> str:
|
94
|
+
return f'<{self.__class__.__name__}: {f"{self.__module.spec}:{self.__name}"!r}>'
|
95
|
+
|
96
|
+
class _Module:
|
97
|
+
def __init__(
|
98
|
+
self,
|
99
|
+
spec: '_ImportCaptureImpl.ModuleSpec',
|
100
|
+
*,
|
101
|
+
getattr_handler: ta.Callable[['_ImportCaptureImpl._Module', str], ta.Any] | None = None,
|
102
|
+
) -> None:
|
103
|
+
super().__init__()
|
104
|
+
|
105
|
+
self.spec = spec
|
106
|
+
|
107
|
+
self.module_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {spec!r}>')
|
108
|
+
if getattr_handler is not None:
|
109
|
+
self.module_obj.__getattr__ = functools.partial(getattr_handler, self) # type: ignore[method-assign] # noqa
|
110
|
+
self.initial_module_dict = dict(self.module_obj.__dict__)
|
111
|
+
|
112
|
+
self.contents: dict[str, _ImportCaptureImpl._ModuleAttr | types.ModuleType] = {}
|
113
|
+
self.imported_whole = False
|
114
|
+
|
115
|
+
def __repr__(self) -> str:
|
116
|
+
return f'{self.__class__.__name__}({self.spec!r})'
|
117
|
+
|
118
|
+
def _get_or_make_module(self, spec: ModuleSpec) -> _Module:
|
119
|
+
try:
|
120
|
+
return self._modules_by_spec[spec]
|
121
|
+
except KeyError:
|
122
|
+
pass
|
123
|
+
|
124
|
+
module = self._Module(
|
125
|
+
spec,
|
126
|
+
getattr_handler=self._handle_module_getattr,
|
127
|
+
)
|
128
|
+
self._modules_by_spec[spec] = module
|
129
|
+
self._modules_by_module_obj[module.module_obj] = module
|
130
|
+
return module
|
131
|
+
|
132
|
+
def _handle_module_getattr(self, module: _Module, attr: str) -> ta.Any:
|
133
|
+
if attr in module.contents:
|
134
|
+
raise ImportCaptureErrors.AttrError(str(module.spec), attr)
|
135
|
+
|
136
|
+
v: _ImportCaptureImpl._ModuleAttr | types.ModuleType
|
137
|
+
if not module.spec.name:
|
138
|
+
if not module.spec.level:
|
139
|
+
raise ImportCaptureError
|
140
|
+
cs = _ImportCaptureImpl.ModuleSpec(attr, module.spec.level)
|
141
|
+
cm = self._get_or_make_module(cs)
|
142
|
+
cm.imported_whole = True
|
143
|
+
v = cm.module_obj
|
144
|
+
|
145
|
+
else:
|
146
|
+
ma = _ImportCaptureImpl._ModuleAttr(module, attr)
|
147
|
+
self._attrs[ma] = (module, attr)
|
148
|
+
v = ma
|
149
|
+
|
150
|
+
module.contents[attr] = v
|
151
|
+
setattr(module.module_obj, attr, v)
|
152
|
+
return v
|
153
|
+
|
154
|
+
def _handle_import(
|
155
|
+
self,
|
156
|
+
module: _Module,
|
157
|
+
*,
|
158
|
+
from_list: ta.Sequence[str] | None,
|
159
|
+
) -> None:
|
160
|
+
if from_list is None:
|
161
|
+
if module.spec.level or not module.spec.name:
|
162
|
+
raise ImportCaptureError
|
163
|
+
|
164
|
+
module.imported_whole = True
|
165
|
+
|
166
|
+
else:
|
167
|
+
for attr in from_list:
|
168
|
+
if attr == '*':
|
169
|
+
raise ImportCaptureErrors.ImportStarForbiddenError(str(module.spec), from_list)
|
170
|
+
|
171
|
+
x = getattr(module.module_obj, attr)
|
172
|
+
|
173
|
+
bad = False
|
174
|
+
if x is not module.contents.get(attr):
|
175
|
+
bad = True
|
176
|
+
if isinstance(x, _ImportCaptureImpl._ModuleAttr):
|
177
|
+
if self._attrs[x] != (module, attr):
|
178
|
+
bad = True
|
179
|
+
elif isinstance(x, types.ModuleType):
|
180
|
+
if x not in self._modules_by_module_obj:
|
181
|
+
bad = True
|
182
|
+
else:
|
183
|
+
bad = True
|
184
|
+
if bad:
|
185
|
+
raise ImportCaptureErrors.AttrError(str(module.spec), attr)
|
186
|
+
|
187
|
+
#
|
188
|
+
|
189
|
+
_MOD_SELF_ATTR: ta.ClassVar[str] = '__import_capture__'
|
190
|
+
|
191
|
+
def _intercept_import(
|
192
|
+
self,
|
193
|
+
name: str,
|
194
|
+
*,
|
195
|
+
globals: ta.Mapping[str, ta.Any] | None = None, # noqa
|
196
|
+
from_list: ta.Sequence[str] | None = None,
|
197
|
+
level: int = 0,
|
198
|
+
) -> types.ModuleType | None:
|
199
|
+
if not (
|
200
|
+
globals is not None and
|
201
|
+
globals.get(self._MOD_SELF_ATTR) is self
|
202
|
+
):
|
203
|
+
return None
|
204
|
+
|
205
|
+
spec = _ImportCaptureImpl.ModuleSpec(name, level)
|
206
|
+
module = self._get_or_make_module(spec)
|
207
|
+
|
208
|
+
self._handle_import(
|
209
|
+
module,
|
210
|
+
from_list=from_list,
|
211
|
+
)
|
212
|
+
|
213
|
+
return module.module_obj
|
214
|
+
|
215
|
+
@contextlib.contextmanager
|
216
|
+
def hook_context(
|
217
|
+
self,
|
218
|
+
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
219
|
+
*,
|
220
|
+
forbid_uncaptured_imports: bool = False,
|
221
|
+
) -> ta.Iterator[None]:
|
222
|
+
if self._MOD_SELF_ATTR in mod_globals:
|
223
|
+
raise ImportCaptureErrors.HookError
|
224
|
+
|
225
|
+
old_import = builtins.__import__
|
226
|
+
|
227
|
+
def new_import(
|
228
|
+
name,
|
229
|
+
globals=None, # noqa
|
230
|
+
locals=None, # noqa
|
231
|
+
fromlist=None,
|
232
|
+
level=0,
|
233
|
+
):
|
234
|
+
if (im := self._intercept_import(
|
235
|
+
name,
|
236
|
+
globals=globals,
|
237
|
+
from_list=fromlist,
|
238
|
+
level=level,
|
239
|
+
)) is not None:
|
240
|
+
return im
|
241
|
+
|
242
|
+
if forbid_uncaptured_imports:
|
243
|
+
raise ImportCaptureErrors.UncapturedImportForbiddenError(
|
244
|
+
str(_ImportCaptureImpl.ModuleSpec(name, level)),
|
245
|
+
fromlist,
|
246
|
+
)
|
247
|
+
|
248
|
+
return old_import(
|
249
|
+
name,
|
250
|
+
globals=globals,
|
251
|
+
locals=locals,
|
252
|
+
fromlist=fromlist,
|
253
|
+
level=level,
|
254
|
+
)
|
255
|
+
|
256
|
+
#
|
257
|
+
|
258
|
+
mod_globals[self._MOD_SELF_ATTR] = self
|
259
|
+
builtins.__import__ = new_import
|
260
|
+
|
261
|
+
try:
|
262
|
+
yield
|
263
|
+
|
264
|
+
finally:
|
265
|
+
if not (
|
266
|
+
mod_globals[self._MOD_SELF_ATTR] is self and
|
267
|
+
builtins.__import__ is new_import
|
268
|
+
):
|
269
|
+
raise ImportCaptureErrors.HookError
|
270
|
+
|
271
|
+
del mod_globals[self._MOD_SELF_ATTR]
|
272
|
+
builtins.__import__ = old_import
|
273
|
+
|
274
|
+
#
|
275
|
+
|
276
|
+
def verify_state(
|
277
|
+
self,
|
278
|
+
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
279
|
+
) -> None:
|
280
|
+
for m in self._modules_by_spec.values():
|
281
|
+
for a, o in m.module_obj.__dict__.items():
|
282
|
+
try:
|
283
|
+
i = m.initial_module_dict[a]
|
284
|
+
|
285
|
+
except KeyError:
|
286
|
+
if o is not m.contents[a]:
|
287
|
+
raise ImportCaptureErrors.AttrError(str(m.spec), a) from None
|
288
|
+
|
289
|
+
else:
|
290
|
+
if o != i:
|
291
|
+
raise ImportCaptureErrors.AttrError(str(m.spec), a)
|
292
|
+
|
293
|
+
#
|
294
|
+
|
295
|
+
def build_captured(
|
296
|
+
self,
|
297
|
+
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
298
|
+
*,
|
299
|
+
collect_unreferenced: bool = False,
|
300
|
+
) -> 'ImportCapture.Captured':
|
301
|
+
dct: dict[_ImportCaptureImpl._Module, list[tuple[str | None, str]]] = {}
|
302
|
+
|
303
|
+
rem_whole_mods: set[_ImportCaptureImpl._Module] = set()
|
304
|
+
rem_mod_attrs: set[_ImportCaptureImpl._ModuleAttr] = set()
|
305
|
+
if collect_unreferenced:
|
306
|
+
rem_whole_mods.update([m for m in self._modules_by_spec.values() if m.imported_whole])
|
307
|
+
rem_mod_attrs.update(self._attrs)
|
308
|
+
|
309
|
+
for attr, obj in mod_globals.items():
|
310
|
+
if isinstance(obj, _ImportCaptureImpl._ModuleAttr):
|
311
|
+
try:
|
312
|
+
m, a = self._attrs[obj]
|
313
|
+
except KeyError:
|
314
|
+
raise ImportCaptureErrors.AttrError(None, attr) from None
|
315
|
+
dct.setdefault(m, []).append((a, attr))
|
316
|
+
rem_mod_attrs.discard(obj)
|
317
|
+
|
318
|
+
elif isinstance(obj, _ImportCaptureImpl._Module):
|
319
|
+
raise ImportCaptureErrors.AttrError(None, attr) from None
|
320
|
+
|
321
|
+
elif isinstance(obj, types.ModuleType):
|
322
|
+
try:
|
323
|
+
m = self._modules_by_module_obj[obj]
|
324
|
+
except KeyError:
|
325
|
+
continue
|
326
|
+
if not m.imported_whole:
|
327
|
+
raise RuntimeError(f'ImportCapture module {m.spec!r} not imported_whole')
|
328
|
+
dct.setdefault(m, []).append((None, attr))
|
329
|
+
rem_whole_mods.discard(m)
|
330
|
+
|
331
|
+
lst: list[ImportCapture.Import] = []
|
332
|
+
for m, ts in dct.items():
|
333
|
+
if not m.spec.name:
|
334
|
+
if not m.spec.level:
|
335
|
+
raise ImportCaptureError
|
336
|
+
for imp_attr, as_attr in ts:
|
337
|
+
if not imp_attr:
|
338
|
+
raise RuntimeError
|
339
|
+
lst.append(ImportCapture.Import(
|
340
|
+
'.' * m.spec.level + imp_attr,
|
341
|
+
[(None, as_attr)],
|
342
|
+
))
|
343
|
+
|
344
|
+
else:
|
345
|
+
lst.append(ImportCapture.Import(
|
346
|
+
str(m.spec),
|
347
|
+
ts,
|
348
|
+
))
|
349
|
+
|
350
|
+
unreferenced: dict[str, list[str | None]] | None = None
|
351
|
+
if collect_unreferenced and (rem_whole_mods or rem_mod_attrs):
|
352
|
+
unreferenced = {}
|
353
|
+
for m in rem_whole_mods:
|
354
|
+
unreferenced.setdefault(str(m.spec), []).append(None)
|
355
|
+
for ma in rem_mod_attrs:
|
356
|
+
m, a = self._attrs[ma]
|
357
|
+
unreferenced.setdefault(str(m.spec), []).append(a)
|
358
|
+
|
359
|
+
return ImportCapture.Captured(
|
360
|
+
lst,
|
361
|
+
unreferenced,
|
362
|
+
)
|
363
|
+
|
364
|
+
|
365
|
+
class ImportCapture:
|
366
|
+
"""
|
367
|
+
This is a bit extreme, but worth it. For simplicity, it currently relies on temporarily patching
|
368
|
+
`__builtins__.__import__` for the duration of its context manager, but it can be switched to use any number of other
|
369
|
+
import hooks (like `sys.meta_path`). It does not rely on any permanent modification to import machinery, only for
|
370
|
+
the duration of its capture.
|
371
|
+
"""
|
372
|
+
|
373
|
+
class Import(ta.NamedTuple):
|
374
|
+
spec: str
|
375
|
+
attrs: ta.Sequence[tuple[str | None, str]]
|
376
|
+
|
377
|
+
class Captured(ta.NamedTuple):
|
378
|
+
imports: ta.Sequence['ImportCapture.Import']
|
379
|
+
unreferenced: ta.Mapping[str, ta.Sequence[str | None]] | None
|
380
|
+
|
381
|
+
@property
|
382
|
+
def attrs(self) -> ta.Iterator[str]:
|
383
|
+
for pi in self.imports:
|
384
|
+
for _, a in pi.attrs:
|
385
|
+
yield a
|
386
|
+
|
387
|
+
#
|
388
|
+
|
389
|
+
def __init__(
|
390
|
+
self,
|
391
|
+
mod_globals: ta.MutableMapping[str, ta.Any],
|
392
|
+
*,
|
393
|
+
disable: bool = False,
|
394
|
+
) -> None:
|
395
|
+
super().__init__()
|
396
|
+
|
397
|
+
self._mod_globals = mod_globals
|
398
|
+
|
399
|
+
self._disabled = disable
|
400
|
+
|
401
|
+
@property
|
402
|
+
def disabled(self) -> bool:
|
403
|
+
return self._disabled
|
404
|
+
|
405
|
+
#
|
406
|
+
|
407
|
+
class _Result(ta.NamedTuple):
|
408
|
+
captured: 'ImportCapture.Captured'
|
409
|
+
|
410
|
+
_result_: _Result | None = None
|
411
|
+
|
412
|
+
@property
|
413
|
+
def _result(self) -> _Result:
|
414
|
+
if (rs := self._result_) is None:
|
415
|
+
raise ImportCaptureErrors.CaptureInProgressError
|
416
|
+
return rs
|
417
|
+
|
418
|
+
@property
|
419
|
+
def is_complete(self) -> bool:
|
420
|
+
return self._result_ is not None
|
421
|
+
|
422
|
+
@property
|
423
|
+
def captured(self) -> Captured:
|
424
|
+
return self._result.captured
|
425
|
+
|
426
|
+
#
|
427
|
+
|
428
|
+
@contextlib.contextmanager
|
429
|
+
def capture(
|
430
|
+
self,
|
431
|
+
*,
|
432
|
+
unreferenced_callback: ta.Callable[[ta.Mapping[str, ta.Sequence[str | None]]], None] | None = None,
|
433
|
+
raise_unreferenced: bool = False,
|
434
|
+
) -> ta.Iterator[ta.Self]:
|
435
|
+
if self._result_ is not None:
|
436
|
+
raise ImportCaptureError('capture already complete')
|
437
|
+
|
438
|
+
if self._disabled:
|
439
|
+
self._result_ = ImportCapture._Result(
|
440
|
+
ImportCapture.Captured(
|
441
|
+
[],
|
442
|
+
None,
|
443
|
+
),
|
444
|
+
)
|
445
|
+
yield self
|
446
|
+
return
|
447
|
+
|
448
|
+
cap = _ImportCaptureImpl()
|
449
|
+
|
450
|
+
with cap.hook_context(self._mod_globals):
|
451
|
+
yield self
|
452
|
+
|
453
|
+
cap.verify_state(self._mod_globals)
|
454
|
+
|
455
|
+
blt = cap.build_captured(
|
456
|
+
self._mod_globals,
|
457
|
+
collect_unreferenced=unreferenced_callback is not None or raise_unreferenced,
|
458
|
+
)
|
459
|
+
|
460
|
+
if blt.unreferenced:
|
461
|
+
if unreferenced_callback:
|
462
|
+
unreferenced_callback(blt.unreferenced)
|
463
|
+
if raise_unreferenced:
|
464
|
+
raise ImportCaptureErrors.UnreferencedImportsError(blt.unreferenced)
|
465
|
+
|
466
|
+
for pi in blt.imports:
|
467
|
+
for _, a in pi.attrs:
|
468
|
+
del self._mod_globals[a]
|
469
|
+
|
470
|
+
self._result_ = ImportCapture._Result(
|
471
|
+
blt,
|
472
|
+
)
|
473
|
+
|
474
|
+
#
|
475
|
+
|
476
|
+
def update_exports(self) -> None:
|
477
|
+
cap = self._result.captured
|
478
|
+
|
479
|
+
try:
|
480
|
+
al: ta.Any = self._mod_globals['__all__']
|
481
|
+
except KeyError:
|
482
|
+
al = self._mod_globals['__all__'] = [k for k in self._mod_globals if not k.startswith('_')]
|
483
|
+
else:
|
484
|
+
if not isinstance(al, ta.MutableSequence):
|
485
|
+
al = self._mod_globals['__all__'] = list(al)
|
486
|
+
|
487
|
+
al_s = set(al)
|
488
|
+
for a in cap.attrs:
|
489
|
+
if a not in al_s:
|
490
|
+
al.append(a)
|
491
|
+
al_s.add(a)
|
omlish/lang/imports/lazy.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
import importlib.util
|
2
|
-
import types
|
3
2
|
import typing as ta
|
4
3
|
|
5
4
|
|
@@ -40,27 +39,3 @@ def lazy_import(
|
|
40
39
|
return mod
|
41
40
|
|
42
41
|
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[method-assign]
|
66
|
-
return lmod
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import contextlib
|
2
|
+
import importlib.util
|
3
|
+
import types
|
4
|
+
import typing as ta
|
5
|
+
|
6
|
+
from .capture import ImportCapture
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
|
11
|
+
|
12
|
+
def proxy_import(
|
13
|
+
spec: str,
|
14
|
+
package: str | None = None,
|
15
|
+
extras: ta.Iterable[str] | None = None,
|
16
|
+
) -> types.ModuleType:
|
17
|
+
if isinstance(extras, str):
|
18
|
+
raise TypeError(extras)
|
19
|
+
|
20
|
+
omod = None
|
21
|
+
|
22
|
+
def __getattr__(att): # noqa
|
23
|
+
nonlocal omod
|
24
|
+
if omod is None:
|
25
|
+
omod = importlib.import_module(spec, package=package)
|
26
|
+
if extras:
|
27
|
+
for x in extras:
|
28
|
+
importlib.import_module(f'{spec}.{x}', package=package)
|
29
|
+
return getattr(omod, att)
|
30
|
+
|
31
|
+
lmod = types.ModuleType(spec)
|
32
|
+
lmod.__getattr__ = __getattr__ # type: ignore[method-assign]
|
33
|
+
return lmod
|
34
|
+
|
35
|
+
|
36
|
+
##
|
37
|
+
|
38
|
+
|
39
|
+
@contextlib.contextmanager
|
40
|
+
def auto_proxy_import(
|
41
|
+
mod_globals: ta.MutableMapping[str, ta.Any],
|
42
|
+
*,
|
43
|
+
disable: bool = False,
|
44
|
+
|
45
|
+
unreferenced_callback: ta.Callable[[ta.Mapping[str, ta.Sequence[str | None]]], None] | None = None,
|
46
|
+
raise_unreferenced: bool = False,
|
47
|
+
) -> ta.Iterator[ImportCapture]:
|
48
|
+
inst = ImportCapture(
|
49
|
+
mod_globals,
|
50
|
+
disable=disable,
|
51
|
+
)
|
52
|
+
|
53
|
+
with inst.capture(
|
54
|
+
unreferenced_callback=unreferenced_callback,
|
55
|
+
raise_unreferenced=raise_unreferenced,
|
56
|
+
):
|
57
|
+
yield inst
|
58
|
+
|
59
|
+
pkg = mod_globals.get('__package__')
|
60
|
+
for pi in inst.captured.imports:
|
61
|
+
for sa, ma in pi.attrs:
|
62
|
+
mod_globals[ma] = proxy_import(pi.spec + (('.' + sa) if sa is not None else ''), pkg)
|