omlish 0.0.0.dev458__py3-none-any.whl → 0.0.0.dev460__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/diag/pydevd.py +23 -1
- omlish/lang/imports/_capture.cc +5 -4
- omlish/lang/imports/capture.py +361 -170
- omlish/lang/imports/proxy.py +445 -152
- omlish/lang/lazyglobals.py +22 -9
- omlish/term/vt100/terminal.py +0 -3
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/METADATA +3 -3
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/RECORD +13 -13
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev458.dist-info → omlish-0.0.0.dev460.dist-info}/top_level.txt +0 -0
omlish/lang/imports/capture.py
CHANGED
@@ -15,6 +15,7 @@ Possible alt impls:
|
|
15
15
|
import builtins
|
16
16
|
import contextlib
|
17
17
|
import functools
|
18
|
+
import importlib.util
|
18
19
|
import sys
|
19
20
|
import threading
|
20
21
|
import types
|
@@ -46,14 +47,27 @@ class ImportCaptureErrors:
|
|
46
47
|
return f'{self.__class__.__qualname__}(module={self.module!r}, name={self.name!r})'
|
47
48
|
|
48
49
|
class ImportError(ImportCaptureError): # noqa
|
49
|
-
def __init__(
|
50
|
+
def __init__(
|
51
|
+
self,
|
52
|
+
name: str,
|
53
|
+
*,
|
54
|
+
level: int | None = None,
|
55
|
+
from_list: ta.Sequence[str] | None,
|
56
|
+
) -> None:
|
50
57
|
super().__init__()
|
51
58
|
|
52
|
-
self.
|
59
|
+
self.name = name
|
60
|
+
self.level = level
|
53
61
|
self.from_list = from_list
|
54
62
|
|
55
63
|
def __repr__(self) -> str:
|
56
|
-
return
|
64
|
+
return ''.join([
|
65
|
+
f'{self.__class__.__qualname__}(',
|
66
|
+
f'name={self.name!r}',
|
67
|
+
*([f', level={self.level!r}'] if self.level is not None else []),
|
68
|
+
*([f', from_list={self.from_list!r}'] if self.from_list is not None else []),
|
69
|
+
')',
|
70
|
+
])
|
57
71
|
|
58
72
|
class ImportStarForbiddenError(ImportError):
|
59
73
|
pass
|
@@ -62,7 +76,7 @@ class ImportCaptureErrors:
|
|
62
76
|
pass
|
63
77
|
|
64
78
|
class UnreferencedImportsError(ImportCaptureError):
|
65
|
-
def __init__(self, unreferenced: ta.
|
79
|
+
def __init__(self, unreferenced: ta.Sequence[str]) -> None:
|
66
80
|
super().__init__()
|
67
81
|
|
68
82
|
self.unreferenced = unreferenced
|
@@ -78,136 +92,138 @@ class ImportCaptureErrors:
|
|
78
92
|
|
79
93
|
|
80
94
|
class _ImportCaptureHook:
|
81
|
-
class ModuleSpec(ta.NamedTuple):
|
82
|
-
name: str
|
83
|
-
level: int
|
84
|
-
|
85
|
-
def __str__(self) -> str:
|
86
|
-
return f'{"." * self.level}{self.name}'
|
87
|
-
|
88
|
-
def __repr__(self) -> str:
|
89
|
-
return repr(str(self))
|
90
|
-
|
91
95
|
def __init__(
|
92
96
|
self,
|
93
97
|
*,
|
98
|
+
package: str | None = None,
|
94
99
|
forbid_uncaptured_imports: bool = False,
|
95
100
|
) -> None:
|
96
101
|
super().__init__()
|
97
102
|
|
103
|
+
self._package = package
|
98
104
|
self._forbid_uncaptured_imports = forbid_uncaptured_imports
|
99
105
|
|
100
|
-
self.
|
106
|
+
self._modules_by_name: dict[str, _ImportCaptureHook._Module] = {}
|
101
107
|
self._modules_by_module_obj: dict[types.ModuleType, _ImportCaptureHook._Module] = {}
|
102
108
|
|
103
|
-
self._attrs: dict[_ImportCaptureHook._ModuleAttr, tuple[_ImportCaptureHook._Module, str]] = {}
|
104
|
-
|
105
109
|
#
|
106
110
|
|
107
|
-
class
|
111
|
+
class _Module:
|
108
112
|
def __init__(
|
109
113
|
self,
|
110
|
-
module: '_ImportCaptureHook._Module',
|
111
114
|
name: str,
|
115
|
+
getattr_handler: ta.Callable[['_ImportCaptureHook._Module', str], ta.Any],
|
116
|
+
*,
|
117
|
+
parent: ta.Optional['_ImportCaptureHook._Module'] = None,
|
112
118
|
) -> None:
|
113
119
|
super().__init__()
|
114
120
|
|
115
|
-
|
116
|
-
|
121
|
+
if name.startswith('.'):
|
122
|
+
raise ImportCaptureError
|
117
123
|
|
118
|
-
|
119
|
-
|
124
|
+
self.name = name
|
125
|
+
self.parent = parent
|
120
126
|
|
121
|
-
|
122
|
-
|
123
|
-
self,
|
124
|
-
spec: '_ImportCaptureHook.ModuleSpec',
|
125
|
-
*,
|
126
|
-
getattr_handler: ta.Callable[['_ImportCaptureHook._Module', str], ta.Any] | None = None,
|
127
|
-
) -> None:
|
128
|
-
super().__init__()
|
127
|
+
self.base_name = name.rpartition('.')[2]
|
128
|
+
self.root: _ImportCaptureHook._Module = parent.root if parent is not None else self # noqa
|
129
129
|
|
130
|
-
self.
|
130
|
+
self.children: dict[str, _ImportCaptureHook._Module] = {}
|
131
|
+
self.descendants: set[_ImportCaptureHook._Module] = set()
|
131
132
|
|
132
|
-
self.module_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {
|
133
|
-
|
134
|
-
|
133
|
+
self.module_obj = types.ModuleType(f'<{self.__class__.__qualname__}: {name}>')
|
134
|
+
self.module_obj.__file__ = None
|
135
|
+
self.module_obj.__getattr__ = functools.partial(getattr_handler, self) # type: ignore[method-assign] # noqa
|
135
136
|
self.initial_module_dict = dict(self.module_obj.__dict__)
|
136
137
|
|
137
|
-
self.
|
138
|
-
self.
|
138
|
+
self.explicit = False
|
139
|
+
self.immediate = False
|
139
140
|
|
140
141
|
def __repr__(self) -> str:
|
141
|
-
return f'{self.__class__.__name__}
|
142
|
+
return f'{self.__class__.__name__}<{self.name}{"!" if self.immediate else "+" if self.explicit else ""}>'
|
142
143
|
|
143
|
-
|
144
|
+
def set_explicit(self) -> None:
|
145
|
+
cur: _ImportCaptureHook._Module | None = self
|
146
|
+
while cur is not None and not cur.explicit:
|
147
|
+
cur.explicit = True
|
148
|
+
cur = cur.parent
|
149
|
+
|
150
|
+
#
|
151
|
+
|
152
|
+
@property
|
153
|
+
def _modules(self) -> ta.Sequence[_Module]:
|
154
|
+
return sorted(self._modules_by_name.values(), key=lambda m: m.name)
|
155
|
+
|
156
|
+
def _get_or_make_module(self, name: str) -> _Module:
|
144
157
|
try:
|
145
|
-
return self.
|
158
|
+
return self._modules_by_name[name]
|
146
159
|
except KeyError:
|
147
160
|
pass
|
148
161
|
|
149
|
-
|
150
|
-
|
151
|
-
|
162
|
+
parent: _ImportCaptureHook._Module | None = None
|
163
|
+
if '.' in name:
|
164
|
+
rest, _, attr = name.rpartition('.')
|
165
|
+
parent = self._get_or_make_module(rest)
|
166
|
+
if attr in parent.children:
|
167
|
+
raise ImportCaptureErrors.AttrError(rest, attr)
|
168
|
+
|
169
|
+
module = _ImportCaptureHook._Module(
|
170
|
+
name,
|
171
|
+
self._handle_module_getattr,
|
172
|
+
parent=parent,
|
152
173
|
)
|
153
|
-
self.
|
174
|
+
self._modules_by_name[name] = module
|
154
175
|
self._modules_by_module_obj[module.module_obj] = module
|
176
|
+
|
177
|
+
if parent is not None:
|
178
|
+
parent.children[module.base_name] = module
|
179
|
+
setattr(parent.module_obj, module.base_name, module.module_obj)
|
180
|
+
parent.root.descendants.add(module)
|
181
|
+
|
155
182
|
return module
|
156
183
|
|
157
|
-
def
|
158
|
-
if attr in module.
|
159
|
-
raise ImportCaptureErrors.AttrError(
|
184
|
+
def _make_child_module(self, module: _Module, attr: str) -> _Module:
|
185
|
+
if attr in module.children:
|
186
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
160
187
|
|
161
|
-
|
162
|
-
if not module.spec.name:
|
163
|
-
if not module.spec.level:
|
164
|
-
raise ImportCaptureError
|
165
|
-
cs = _ImportCaptureHook.ModuleSpec(attr, module.spec.level)
|
166
|
-
cm = self._get_or_make_module(cs)
|
167
|
-
cm.imported_whole = True
|
168
|
-
v = cm.module_obj
|
188
|
+
return self._get_or_make_module(f'{module.name}.{attr}')
|
169
189
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
190
|
+
#
|
191
|
+
|
192
|
+
def _handle_module_getattr(self, module: _Module, attr: str) -> ta.Any:
|
193
|
+
if not module.explicit:
|
194
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
174
195
|
|
175
|
-
module.
|
176
|
-
setattr(module.module_obj, attr, v)
|
177
|
-
return v
|
196
|
+
return self._make_child_module(module, attr).module_obj
|
178
197
|
|
179
198
|
def _handle_import(
|
180
199
|
self,
|
181
|
-
|
200
|
+
name: str,
|
182
201
|
*,
|
183
202
|
from_list: ta.Sequence[str] | None,
|
184
|
-
) ->
|
185
|
-
|
186
|
-
if module.spec.level or not module.spec.name:
|
187
|
-
raise ImportCaptureError
|
203
|
+
) -> types.ModuleType:
|
204
|
+
module = self._get_or_make_module(name)
|
188
205
|
|
189
|
-
|
206
|
+
if from_list is None:
|
207
|
+
module.set_explicit()
|
208
|
+
module.root.immediate = True
|
209
|
+
return module.root.module_obj
|
190
210
|
|
191
211
|
else:
|
192
212
|
for attr in from_list:
|
193
213
|
if attr == '*':
|
194
|
-
raise ImportCaptureErrors.ImportStarForbiddenError(
|
214
|
+
raise ImportCaptureErrors.ImportStarForbiddenError(module.name, from_list=from_list)
|
215
|
+
|
216
|
+
if (cm := module.children.get(attr)) is None:
|
217
|
+
cm = self._make_child_module(module, attr)
|
218
|
+
cm.set_explicit()
|
219
|
+
cm.immediate = True
|
220
|
+
continue
|
195
221
|
|
196
222
|
x = getattr(module.module_obj, attr)
|
223
|
+
if x is not cm.module_obj or x not in self._modules_by_module_obj:
|
224
|
+
raise ImportCaptureErrors.AttrError(module.name, attr)
|
197
225
|
|
198
|
-
|
199
|
-
if x is not module.contents.get(attr):
|
200
|
-
bad = True
|
201
|
-
if isinstance(x, _ImportCaptureHook._ModuleAttr):
|
202
|
-
if self._attrs[x] != (module, attr):
|
203
|
-
bad = True
|
204
|
-
elif isinstance(x, types.ModuleType):
|
205
|
-
if x not in self._modules_by_module_obj:
|
206
|
-
bad = True
|
207
|
-
else:
|
208
|
-
bad = True
|
209
|
-
if bad:
|
210
|
-
raise ImportCaptureErrors.AttrError(str(module.spec), attr)
|
226
|
+
return module.module_obj
|
211
227
|
|
212
228
|
#
|
213
229
|
|
@@ -227,16 +243,16 @@ class _ImportCaptureHook:
|
|
227
243
|
):
|
228
244
|
return None
|
229
245
|
|
230
|
-
|
231
|
-
|
246
|
+
if level:
|
247
|
+
if not self._package:
|
248
|
+
raise ImportCaptureError
|
249
|
+
name = importlib.util.resolve_name(('.' * level) + name, self._package)
|
232
250
|
|
233
|
-
self._handle_import(
|
234
|
-
|
251
|
+
return self._handle_import(
|
252
|
+
name,
|
235
253
|
from_list=from_list,
|
236
254
|
)
|
237
255
|
|
238
|
-
return module.module_obj
|
239
|
-
|
240
256
|
@ta.final
|
241
257
|
@contextlib.contextmanager
|
242
258
|
def hook_context(
|
@@ -271,18 +287,24 @@ class _ImportCaptureHook:
|
|
271
287
|
self,
|
272
288
|
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
273
289
|
) -> None:
|
274
|
-
for m in self.
|
290
|
+
for m in self._modules_by_name.values():
|
291
|
+
if m.immediate and not m.explicit:
|
292
|
+
raise ImportCaptureError
|
293
|
+
|
294
|
+
if not m.explicit and m.children:
|
295
|
+
raise ImportCaptureError
|
296
|
+
|
275
297
|
for a, o in m.module_obj.__dict__.items():
|
276
298
|
try:
|
277
299
|
i = m.initial_module_dict[a]
|
278
300
|
|
279
301
|
except KeyError:
|
280
|
-
if o is not m.
|
281
|
-
raise ImportCaptureErrors.AttrError(
|
302
|
+
if o is not m.children[a].module_obj:
|
303
|
+
raise ImportCaptureErrors.AttrError(m.name, a) from None
|
282
304
|
|
283
305
|
else:
|
284
306
|
if o != i:
|
285
|
-
raise ImportCaptureErrors.AttrError(
|
307
|
+
raise ImportCaptureErrors.AttrError(m.name, a)
|
286
308
|
|
287
309
|
#
|
288
310
|
|
@@ -292,24 +314,20 @@ class _ImportCaptureHook:
|
|
292
314
|
*,
|
293
315
|
collect_unreferenced: bool = False,
|
294
316
|
) -> 'ImportCapture.Captured':
|
295
|
-
|
296
|
-
|
297
|
-
rem_whole_mods: set[_ImportCaptureHook._Module] = set()
|
298
|
-
rem_mod_attrs: set[_ImportCaptureHook._ModuleAttr] = set()
|
317
|
+
rem_explicit_mods: set[_ImportCaptureHook._Module] = set()
|
299
318
|
if collect_unreferenced:
|
300
|
-
|
301
|
-
|
319
|
+
rem_explicit_mods.update(
|
320
|
+
m for m in self._modules_by_name.values()
|
321
|
+
if m.immediate
|
322
|
+
and m.parent is not None # No good way to tell if user did `import a.b.c` or `import a.b.c as c`
|
323
|
+
)
|
302
324
|
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
m, a = self._attrs[obj]
|
307
|
-
except KeyError:
|
308
|
-
raise ImportCaptureErrors.AttrError(None, attr) from None
|
309
|
-
dct.setdefault(m, []).append((a, attr))
|
310
|
-
rem_mod_attrs.discard(obj)
|
325
|
+
#
|
326
|
+
|
327
|
+
dct: dict[_ImportCaptureHook._Module, list[tuple[str | None, str]]] = {}
|
311
328
|
|
312
|
-
|
329
|
+
for attr, obj in mod_globals.items():
|
330
|
+
if isinstance(obj, _ImportCaptureHook._Module):
|
313
331
|
raise ImportCaptureErrors.AttrError(None, attr) from None
|
314
332
|
|
315
333
|
elif isinstance(obj, types.ModuleType):
|
@@ -317,41 +335,80 @@ class _ImportCaptureHook:
|
|
317
335
|
m = self._modules_by_module_obj[obj]
|
318
336
|
except KeyError:
|
319
337
|
continue
|
320
|
-
if not m.imported_whole:
|
321
|
-
raise RuntimeError(f'ImportCapture module {m.spec!r} not imported_whole')
|
322
|
-
dct.setdefault(m, []).append((None, attr))
|
323
|
-
rem_whole_mods.discard(m)
|
324
|
-
|
325
|
-
lst: list[ImportCapture.Import] = []
|
326
|
-
for m, ts in dct.items():
|
327
|
-
if not m.spec.name:
|
328
|
-
if not m.spec.level:
|
329
|
-
raise ImportCaptureError
|
330
|
-
for imp_attr, as_attr in ts:
|
331
|
-
if not imp_attr:
|
332
|
-
raise RuntimeError
|
333
|
-
lst.append(ImportCapture.Import(
|
334
|
-
'.' * m.spec.level + imp_attr,
|
335
|
-
[(None, as_attr)],
|
336
|
-
))
|
337
338
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
339
|
+
if m.explicit:
|
340
|
+
dct.setdefault(m, []).append((None, attr))
|
341
|
+
if m in rem_explicit_mods:
|
342
|
+
# Remove everything reachable from this root *except* items imported immediately, such as
|
343
|
+
# `from x import y` - those still need to be immediately reachable.
|
344
|
+
rem_explicit_mods -= {dm for dm in m.descendants if not dm.immediate}
|
345
|
+
rem_explicit_mods.remove(m)
|
346
|
+
|
347
|
+
else:
|
348
|
+
p = m.parent
|
349
|
+
if p is None or not p.explicit:
|
350
|
+
raise ImportCaptureError
|
351
|
+
dct.setdefault(p, []).append((m.base_name, attr))
|
352
|
+
|
353
|
+
#
|
354
|
+
|
355
|
+
mods: dict[str, ImportCapture.Module] = {}
|
356
|
+
|
357
|
+
def build_import_module(m: _ImportCaptureHook._Module) -> ImportCapture.Module:
|
358
|
+
children: dict[str, ImportCapture.Module] = {}
|
359
|
+
attrs: list[str] = []
|
360
|
+
for cm in sorted(m.children.values(), key=lambda cm: cm.name):
|
361
|
+
if not cm.explicit:
|
362
|
+
attrs.append(cm.base_name)
|
363
|
+
else:
|
364
|
+
children[cm.base_name] = build_import_module(cm)
|
365
|
+
|
366
|
+
mod = ImportCapture.Module(
|
367
|
+
m.name,
|
368
|
+
children or None,
|
369
|
+
attrs or None,
|
370
|
+
)
|
371
|
+
|
372
|
+
if m.parent is None:
|
373
|
+
mod.parent = None
|
374
|
+
for c in children.values():
|
375
|
+
c.parent = mod
|
376
|
+
|
377
|
+
mods[mod.name] = mod
|
378
|
+
return mod
|
379
|
+
|
380
|
+
root_mods: dict[str, ImportCapture.Module] = {
|
381
|
+
m.base_name: build_import_module(m)
|
382
|
+
for m in self._modules_by_name.values()
|
383
|
+
if m.parent is None
|
384
|
+
}
|
385
|
+
|
386
|
+
mods = dict(sorted(mods.items(), key=lambda t: t[0]))
|
387
|
+
root_mods = dict(sorted(root_mods.items(), key=lambda t: t[0]))
|
388
|
+
|
389
|
+
#
|
390
|
+
|
391
|
+
imps: list[ImportCapture.Import] = []
|
392
|
+
|
393
|
+
for m, ts in sorted(dct.items(), key=lambda t: t[0].name):
|
394
|
+
imps.append(ImportCapture.Import(
|
395
|
+
mods[m.name],
|
396
|
+
[r for l, r in ts if l is None] or None,
|
397
|
+
[(l, r) for l, r in ts if l is not None] or None,
|
398
|
+
))
|
399
|
+
|
400
|
+
#
|
401
|
+
|
402
|
+
unreferenced: list[str] | None = None
|
403
|
+
if collect_unreferenced and rem_explicit_mods:
|
404
|
+
unreferenced = sorted(m.name for m in rem_explicit_mods)
|
352
405
|
|
353
406
|
return ImportCapture.Captured(
|
354
|
-
|
407
|
+
{i.module.name: i for i in imps},
|
408
|
+
|
409
|
+
mods,
|
410
|
+
root_mods,
|
411
|
+
|
355
412
|
unreferenced,
|
356
413
|
)
|
357
414
|
|
@@ -360,6 +417,14 @@ class _ImportCaptureHook:
|
|
360
417
|
|
361
418
|
|
362
419
|
class _AbstractBuiltinsImportCaptureHook(_ImportCaptureHook):
|
420
|
+
def __init__(
|
421
|
+
self,
|
422
|
+
*,
|
423
|
+
_frame: types.FrameType | None = None,
|
424
|
+
**kwargs: ta.Any,
|
425
|
+
) -> None:
|
426
|
+
super().__init__(**kwargs)
|
427
|
+
|
363
428
|
def _new_import(
|
364
429
|
self,
|
365
430
|
old_import,
|
@@ -379,8 +444,9 @@ class _AbstractBuiltinsImportCaptureHook(_ImportCaptureHook):
|
|
379
444
|
|
380
445
|
if self._forbid_uncaptured_imports:
|
381
446
|
raise ImportCaptureErrors.UncapturedImportForbiddenError(
|
382
|
-
|
383
|
-
|
447
|
+
name,
|
448
|
+
level=level,
|
449
|
+
from_list=fromlist,
|
384
450
|
)
|
385
451
|
|
386
452
|
return old_import(
|
@@ -515,11 +581,24 @@ class _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook(_AbstractBuiltinsImport
|
|
515
581
|
#
|
516
582
|
|
517
583
|
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
584
|
+
_cext_: ta.Any
|
585
|
+
|
586
|
+
|
587
|
+
def _cext() -> ta.Any:
|
588
|
+
global _cext_
|
589
|
+
try:
|
590
|
+
return _cext_
|
591
|
+
except NameError:
|
592
|
+
pass
|
593
|
+
|
594
|
+
cext: ta.Any
|
595
|
+
try:
|
596
|
+
from . import _capture as cext # type: ignore
|
597
|
+
except ImportError:
|
598
|
+
cext = None
|
599
|
+
|
600
|
+
_cext_ = cext
|
601
|
+
return cext
|
523
602
|
|
524
603
|
|
525
604
|
class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
@@ -539,7 +618,7 @@ class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
|
539
618
|
frame: types.FrameType,
|
540
619
|
new_builtins: dict[str, ta.Any],
|
541
620
|
) -> bool:
|
542
|
-
return
|
621
|
+
return _cext()._set_frame_builtins(frame, frame.f_builtins, new_builtins) # noqa
|
543
622
|
|
544
623
|
@contextlib.contextmanager
|
545
624
|
def _hook_context(
|
@@ -569,39 +648,155 @@ class _FrameBuiltinsImportCaptureHook(_AbstractBuiltinsImportCaptureHook):
|
|
569
648
|
#
|
570
649
|
|
571
650
|
|
651
|
+
_CAPTURE_IMPLS: ta.Mapping[str, type[_AbstractBuiltinsImportCaptureHook]] = {
|
652
|
+
'cext': _FrameBuiltinsImportCaptureHook,
|
653
|
+
'somewhat_safe': _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook,
|
654
|
+
'unsafe': _UnsafeGlobalBuiltinsImportCaptureHook,
|
655
|
+
}
|
656
|
+
|
657
|
+
|
572
658
|
def _new_import_capture_hook(
|
573
659
|
mod_globals: ta.MutableMapping[str, ta.Any], # noqa
|
574
660
|
*,
|
575
661
|
stack_offset: int = 0,
|
662
|
+
capture_impl: str | None = None,
|
576
663
|
**kwargs: ta.Any,
|
577
664
|
) -> '_ImportCaptureHook':
|
578
|
-
|
579
|
-
|
580
|
-
|
665
|
+
if '_frame' not in kwargs:
|
666
|
+
frame: types.FrameType | None = sys._getframe(1 + stack_offset) # noqa
|
667
|
+
if frame is None or frame.f_globals is not mod_globals:
|
668
|
+
raise ImportCaptureError("Can't find importing frame")
|
669
|
+
kwargs['_frame'] = frame
|
670
|
+
|
671
|
+
kwargs.setdefault('package', mod_globals.get('__package__'))
|
581
672
|
|
582
|
-
|
583
|
-
|
673
|
+
cls: type[_AbstractBuiltinsImportCaptureHook]
|
674
|
+
if capture_impl is not None:
|
675
|
+
cls = _CAPTURE_IMPLS[capture_impl]
|
676
|
+
elif _cext() is not None:
|
677
|
+
cls = _FrameBuiltinsImportCaptureHook
|
678
|
+
else:
|
679
|
+
cls = _SomewhatThreadSafeGlobalBuiltinsImportCaptureHook
|
584
680
|
|
585
|
-
return
|
681
|
+
return cls(**kwargs)
|
586
682
|
|
587
683
|
|
588
684
|
##
|
589
685
|
|
590
686
|
|
687
|
+
ImportCaptureModuleKind: ta.TypeAlias = ta.Literal[
|
688
|
+
'parent',
|
689
|
+
'terminal',
|
690
|
+
'leaf',
|
691
|
+
]
|
692
|
+
|
693
|
+
|
591
694
|
class ImportCapture:
|
592
|
-
|
593
|
-
|
594
|
-
|
695
|
+
@ta.final
|
696
|
+
class Module:
|
697
|
+
def __init__(
|
698
|
+
self,
|
699
|
+
name: str,
|
700
|
+
children: ta.Mapping[str, 'ImportCapture.Module'] | None = None,
|
701
|
+
attrs: ta.Sequence[str] | None = None,
|
702
|
+
) -> None:
|
703
|
+
self.name = name
|
704
|
+
self.children = children
|
705
|
+
self.attrs = attrs
|
706
|
+
|
707
|
+
self.base_name = name.rpartition('.')[2]
|
708
|
+
|
709
|
+
if not self.children and not self.attrs:
|
710
|
+
self.kind = 'leaf'
|
711
|
+
elif not self.children or all(c.kind == 'leaf' for c in self.children.values()):
|
712
|
+
self.kind = 'terminal'
|
713
|
+
else:
|
714
|
+
self.kind = 'parent'
|
715
|
+
|
716
|
+
parent: ta.Optional['ImportCapture.Module']
|
717
|
+
|
718
|
+
kind: ImportCaptureModuleKind
|
719
|
+
|
720
|
+
def __repr__(self) -> str:
|
721
|
+
return ''.join([
|
722
|
+
f'{self.__class__.__name__}(',
|
723
|
+
f'{self.name!r}',
|
724
|
+
f', :{self.kind}',
|
725
|
+
*([f', children=[{", ".join(map(repr, self.children))}]'] if self.children else []),
|
726
|
+
*([f', attrs={self.attrs!r}'] if self.attrs else []),
|
727
|
+
')',
|
728
|
+
])
|
729
|
+
|
730
|
+
_root: 'ImportCapture.Module'
|
731
|
+
|
732
|
+
@property
|
733
|
+
def root(self) -> 'ImportCapture.Module':
|
734
|
+
try:
|
735
|
+
return self._root
|
736
|
+
except AttributeError:
|
737
|
+
pass
|
738
|
+
|
739
|
+
root = self
|
740
|
+
while root.parent is not None:
|
741
|
+
root = root.parent
|
742
|
+
self._root = root
|
743
|
+
return root
|
744
|
+
|
745
|
+
@ta.final
|
746
|
+
class Import:
|
747
|
+
def __init__(
|
748
|
+
self,
|
749
|
+
module: 'ImportCapture.Module',
|
750
|
+
as_: ta.Sequence[str] | None,
|
751
|
+
attrs: ta.Sequence[tuple[str, str]] | None, # ('foo', 'bar') -> `import foo as bar` - explicitly not a dict # noqa
|
752
|
+
) -> None:
|
753
|
+
self.module = module
|
754
|
+
self.as_ = as_
|
755
|
+
self.attrs = attrs
|
595
756
|
|
596
|
-
|
597
|
-
|
598
|
-
|
757
|
+
def __repr__(self) -> str:
|
758
|
+
return ''.join([
|
759
|
+
f'{self.__class__.__name__}(',
|
760
|
+
f'{self.module.name!r}',
|
761
|
+
*([f', as_={self.as_!r}'] if self.as_ else []),
|
762
|
+
*([f', attrs={self.attrs!r}'] if self.attrs else []),
|
763
|
+
')',
|
764
|
+
])
|
765
|
+
|
766
|
+
@ta.final
|
767
|
+
class Captured:
|
768
|
+
def __init__(
|
769
|
+
self,
|
770
|
+
|
771
|
+
imports: ta.Mapping[str, 'ImportCapture.Import'],
|
772
|
+
|
773
|
+
modules: ta.Mapping[str, 'ImportCapture.Module'],
|
774
|
+
root_modules: ta.Mapping[str, 'ImportCapture.Module'],
|
775
|
+
|
776
|
+
unreferenced: ta.Sequence[str] | None,
|
777
|
+
) -> None:
|
778
|
+
self.imports = imports
|
779
|
+
|
780
|
+
self.modules = modules
|
781
|
+
self.root_modules = root_modules
|
782
|
+
|
783
|
+
self.unreferenced = unreferenced
|
599
784
|
|
600
785
|
@property
|
601
786
|
def attrs(self) -> ta.Iterator[str]:
|
602
|
-
for pi in self.imports:
|
603
|
-
|
604
|
-
yield
|
787
|
+
for pi in self.imports.values():
|
788
|
+
if pi.as_:
|
789
|
+
yield from pi.as_
|
790
|
+
if pi.attrs:
|
791
|
+
for _, a in pi.attrs:
|
792
|
+
yield a
|
793
|
+
|
794
|
+
EMPTY_CAPTURED: ta.ClassVar[Captured] = Captured(
|
795
|
+
{},
|
796
|
+
{},
|
797
|
+
{},
|
798
|
+
None,
|
799
|
+
)
|
605
800
|
|
606
801
|
#
|
607
802
|
|
@@ -651,7 +846,7 @@ class ImportCapture:
|
|
651
846
|
def capture(
|
652
847
|
self,
|
653
848
|
*,
|
654
|
-
unreferenced_callback: ta.Callable[[ta.
|
849
|
+
unreferenced_callback: ta.Callable[[ta.Sequence[str]], None] | None = None,
|
655
850
|
raise_unreferenced: bool = False,
|
656
851
|
) -> ta.Iterator[ta.Self]:
|
657
852
|
if self._result_ is not None:
|
@@ -659,10 +854,7 @@ class ImportCapture:
|
|
659
854
|
|
660
855
|
if self._disabled:
|
661
856
|
self._result_ = ImportCapture._Result(
|
662
|
-
ImportCapture.
|
663
|
-
[],
|
664
|
-
None,
|
665
|
-
),
|
857
|
+
ImportCapture.EMPTY_CAPTURED,
|
666
858
|
)
|
667
859
|
yield self
|
668
860
|
return
|
@@ -683,9 +875,8 @@ class ImportCapture:
|
|
683
875
|
if raise_unreferenced:
|
684
876
|
raise ImportCaptureErrors.UnreferencedImportsError(blt.unreferenced)
|
685
877
|
|
686
|
-
for
|
687
|
-
|
688
|
-
del self._mod_globals[a]
|
878
|
+
for a in blt.attrs:
|
879
|
+
del self._mod_globals[a]
|
689
880
|
|
690
881
|
self._result_ = ImportCapture._Result(
|
691
882
|
blt,
|