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/lang/imports.py DELETED
@@ -1,418 +0,0 @@
1
- """
2
- TODO:
3
- - proxy_init 'as' alias support - attrs of (src, dst)
4
- - use importlib.util.resolve_name
5
- """
6
- import contextlib
7
- import functools
8
- import importlib.util
9
- import sys
10
- import types
11
- import typing as ta
12
-
13
-
14
- ##
15
-
16
-
17
- def can_import(name: str, package: str | None = None) -> bool:
18
- try:
19
- spec = importlib.util.find_spec(name, package)
20
- except ImportError:
21
- return False
22
- else:
23
- return spec is not None
24
-
25
-
26
- ##
27
-
28
-
29
- def lazy_import(
30
- name: str,
31
- package: str | None = None,
32
- *,
33
- optional: bool = False,
34
- cache_failure: bool = False,
35
- ) -> ta.Callable[[], ta.Any]:
36
- result = not_set = object()
37
-
38
- def inner():
39
- nonlocal result
40
-
41
- if result is not not_set:
42
- if isinstance(result, Exception):
43
- raise result
44
- return result
45
-
46
- try:
47
- mod = importlib.import_module(name, package=package)
48
-
49
- except Exception as e:
50
- if optional:
51
- if cache_failure:
52
- result = None
53
- return None
54
-
55
- if cache_failure:
56
- result = e
57
- raise
58
-
59
- result = mod
60
- return mod
61
-
62
- return inner
63
-
64
-
65
- def proxy_import(
66
- name: str,
67
- package: str | None = None,
68
- extras: ta.Iterable[str] | None = None,
69
- ) -> types.ModuleType:
70
- if isinstance(extras, str):
71
- raise TypeError(extras)
72
-
73
- omod = None
74
-
75
- def __getattr__(att): # noqa
76
- nonlocal omod
77
- if omod is None:
78
- omod = importlib.import_module(name, package=package)
79
- if extras:
80
- for x in extras:
81
- importlib.import_module(f'{name}.{x}', package=package)
82
- return getattr(omod, att)
83
-
84
- lmod = types.ModuleType(name)
85
- lmod.__getattr__ = __getattr__ # type: ignore
86
- return lmod
87
-
88
-
89
- ##
90
-
91
-
92
- SPECIAL_IMPORTABLE: ta.AbstractSet[str] = frozenset([
93
- '__init__.py',
94
- '__main__.py',
95
- ])
96
-
97
-
98
- def yield_importable(
99
- package_root: str,
100
- *,
101
- recursive: bool = False,
102
- filter: ta.Callable[[str], bool] | None = None, # noqa
103
- include_special: bool = False,
104
- ) -> ta.Iterator[str]:
105
- from importlib import resources
106
-
107
- def rec(cur):
108
- if cur.split('.')[-1] == '__pycache__':
109
- return
110
-
111
- try:
112
- module = sys.modules[cur]
113
- except KeyError:
114
- try:
115
- __import__(cur)
116
- except ImportError:
117
- return
118
- module = sys.modules[cur]
119
-
120
- # FIXME: pyox
121
- if getattr(module, '__file__', None) is None:
122
- return
123
-
124
- for file in resources.files(cur).iterdir():
125
- if file.is_file() and file.name.endswith('.py'):
126
- if not (include_special or file.name not in SPECIAL_IMPORTABLE):
127
- continue
128
-
129
- name = cur + '.' + file.name[:-3]
130
- if filter is not None and not filter(name):
131
- continue
132
-
133
- yield name
134
-
135
- elif recursive and file.is_dir():
136
- name = cur + '.' + file.name
137
- if filter is not None and not filter(name):
138
- continue
139
- with contextlib.suppress(ImportError, NotImplementedError):
140
- yield from rec(name)
141
-
142
- yield from rec(package_root)
143
-
144
-
145
- def yield_import_all(
146
- package_root: str,
147
- *,
148
- globals: dict[str, ta.Any] | None = None, # noqa
149
- locals: dict[str, ta.Any] | None = None, # noqa
150
- recursive: bool = False,
151
- filter: ta.Callable[[str], bool] | None = None, # noqa
152
- include_special: bool = False,
153
- ) -> ta.Iterator[str]:
154
- for import_path in yield_importable(
155
- package_root,
156
- recursive=recursive,
157
- filter=filter,
158
- include_special=include_special,
159
- ):
160
- __import__(import_path, globals=globals, locals=locals)
161
- yield import_path
162
-
163
-
164
- def import_all(
165
- package_root: str,
166
- *,
167
- recursive: bool = False,
168
- filter: ta.Callable[[str], bool] | None = None, # noqa
169
- include_special: bool = False,
170
- ) -> None:
171
- for _ in yield_import_all(
172
- package_root,
173
- recursive=recursive,
174
- filter=filter,
175
- include_special=include_special,
176
- ):
177
- pass
178
-
179
-
180
- def try_import(spec: str) -> types.ModuleType | None:
181
- s = spec.lstrip('.')
182
- l = len(spec) - len(s)
183
- try:
184
- return __import__(s, globals(), level=l)
185
- except ImportError:
186
- return None
187
-
188
-
189
- ##
190
-
191
-
192
- def resolve_import_name(name: str, package: str | None = None) -> str:
193
- # FIXME: importlib.util.resolve_name
194
- level = 0
195
-
196
- if name.startswith('.'):
197
- if not package:
198
- raise TypeError("the 'package' argument is required to perform a relative import for {name!r}")
199
- for character in name:
200
- if character != '.':
201
- break
202
- level += 1
203
-
204
- name = name[level:]
205
-
206
- if not isinstance(name, str):
207
- raise TypeError(f'module name must be str, not {type(name)}')
208
- if level < 0:
209
- raise ValueError('level must be >= 0')
210
- if level > 0:
211
- if not isinstance(package, str):
212
- raise TypeError('__package__ not set to a string')
213
- elif not package:
214
- raise ImportError('attempted relative import with no known parent package')
215
- if not name and level == 0:
216
- raise ValueError('Empty module name')
217
-
218
- if level > 0:
219
- bits = package.rsplit('.', level - 1) # type: ignore
220
- if len(bits) < level:
221
- raise ImportError('attempted relative import beyond top-level package')
222
- base = bits[0]
223
- name = f'{base}.{name}' if name else base
224
-
225
- return name
226
-
227
-
228
- ##
229
-
230
-
231
- _REGISTERED_CONDITIONAL_IMPORTS: dict[str, list[str] | None] = {}
232
-
233
-
234
- def _register_conditional_import(when: str, then: str, package: str | None = None) -> None:
235
- wn = resolve_import_name(when, package)
236
- tn = resolve_import_name(then, package)
237
- if tn in sys.modules:
238
- return
239
- if wn in sys.modules:
240
- __import__(tn)
241
- else:
242
- tns = _REGISTERED_CONDITIONAL_IMPORTS.setdefault(wn, [])
243
- if tns is None:
244
- raise Exception(f'Conditional import trigger already cleared: {wn=} {tn=}')
245
- tns.append(tn)
246
-
247
-
248
- def _trigger_conditional_imports(package: str) -> None:
249
- tns = _REGISTERED_CONDITIONAL_IMPORTS.get(package, [])
250
- if tns is None:
251
- raise Exception(f'Conditional import trigger already cleared: {package=}')
252
- _REGISTERED_CONDITIONAL_IMPORTS[package] = None
253
- for tn in tns:
254
- __import__(tn)
255
-
256
-
257
- ##
258
-
259
-
260
- class LazyGlobals:
261
- def __init__(
262
- self,
263
- *,
264
- globals: ta.MutableMapping[str, ta.Any] | None = None, # noqa
265
- update_globals: bool = False,
266
- ) -> None:
267
- super().__init__()
268
-
269
- self._globals = globals
270
- self._update_globals = update_globals
271
-
272
- self._attr_fns: dict[str, ta.Callable[[], ta.Any]] = {}
273
-
274
- @classmethod
275
- def install(cls, globals: ta.MutableMapping[str, ta.Any]) -> 'LazyGlobals': # noqa
276
- try:
277
- xga = globals['__getattr__']
278
- except KeyError:
279
- pass
280
- else:
281
- if not isinstance(xga, cls):
282
- raise RuntimeError(f'Module already has __getattr__ hook: {xga}') # noqa
283
- return xga
284
-
285
- lm = cls(
286
- globals=globals,
287
- update_globals=True,
288
- )
289
-
290
- globals['__getattr__'] = lm
291
-
292
- return lm
293
-
294
- def set_fn(self, attr: str, fn: ta.Callable[[], ta.Any]) -> 'LazyGlobals':
295
- self._attr_fns[attr] = fn
296
- return self
297
-
298
- def get(self, attr: str) -> ta.Any:
299
- try:
300
- fn = self._attr_fns[attr]
301
- except KeyError:
302
- raise AttributeError(attr) from None
303
-
304
- val = fn()
305
-
306
- if self._update_globals and self._globals is not None:
307
- self._globals[attr] = val
308
-
309
- return val
310
-
311
- def __call__(self, attr: str) -> ta.Any:
312
- return self.get(attr)
313
-
314
-
315
- ##
316
-
317
-
318
- class NamePackage(ta.NamedTuple):
319
- name: str
320
- package: str
321
-
322
-
323
- class _ProxyInit:
324
- class _Import(ta.NamedTuple):
325
- pkg: str
326
- attr: str
327
-
328
- def __init__(
329
- self,
330
- lazy_globals: LazyGlobals,
331
- name_package: NamePackage,
332
- ) -> None:
333
- super().__init__()
334
-
335
- self._lazy_globals = lazy_globals
336
- self._name_package = name_package
337
-
338
- self._imps_by_attr: dict[str, _ProxyInit._Import] = {}
339
- self._mods_by_pkgs: dict[str, ta.Any] = {}
340
-
341
- @property
342
- def name_package(self) -> NamePackage:
343
- return self._name_package
344
-
345
- def add(self, package: str, attrs: ta.Iterable[str | tuple[str, str]]) -> None:
346
- if isinstance(attrs, str):
347
- raise TypeError(attrs)
348
-
349
- for attr in attrs:
350
- if isinstance(attr, tuple):
351
- imp_attr, attr = attr
352
- else:
353
- imp_attr = attr
354
-
355
- self._imps_by_attr[attr] = self._Import(package, imp_attr)
356
-
357
- self._lazy_globals.set_fn(attr, functools.partial(self.get, attr))
358
-
359
- def get(self, attr: str) -> ta.Any:
360
- try:
361
- imp = self._imps_by_attr[attr]
362
- except KeyError:
363
- raise AttributeError(attr) # noqa
364
-
365
- try:
366
- mod = self._mods_by_pkgs[imp.pkg]
367
- except KeyError:
368
- mod = importlib.import_module(imp.pkg, package=self._name_package.package)
369
-
370
- val = getattr(mod, imp.attr)
371
-
372
- return val
373
-
374
-
375
- def proxy_init(
376
- globals: ta.MutableMapping[str, ta.Any], # noqa
377
- package: str,
378
- attrs: ta.Iterable[str | tuple[str, str]],
379
- ) -> None:
380
- if isinstance(attrs, str):
381
- raise TypeError(attrs)
382
-
383
- init_name_package = NamePackage(
384
- globals['__name__'],
385
- globals['__package__'],
386
- )
387
-
388
- pi: _ProxyInit
389
- try:
390
- pi = globals['__proxy_init__']
391
-
392
- except KeyError:
393
- pi = _ProxyInit(
394
- LazyGlobals.install(globals),
395
- init_name_package,
396
- )
397
- globals['__proxy_init__'] = pi
398
-
399
- else:
400
- if pi.name_package != init_name_package:
401
- raise Exception(f'Wrong init name: {pi.name_package=} != {init_name_package=}')
402
-
403
- pi.add(package, attrs)
404
-
405
-
406
- ##
407
-
408
-
409
- def get_real_module_name(globals: ta.Mapping[str, ta.Any]) -> str: # noqa
410
- module = sys.modules[globals['__name__']]
411
-
412
- if module.__spec__ and module.__spec__.name:
413
- return module.__spec__.name
414
-
415
- if module.__package__:
416
- return module.__package__
417
-
418
- raise RuntimeError("Can't determine real module name")