]>
Commit | Line | Data |
---|---|---|
1d485a1a | 1 | import collections |
9196cbfe | 2 | import contextlib |
88426d94 | 3 | import functools |
9196cbfe | 4 | import importlib |
5 | import sys | |
6 | import types | |
7 | ||
1d485a1a | 8 | _NO_ATTRIBUTE = object() |
9 | ||
10 | _Package = collections.namedtuple('Package', ('name', 'version')) | |
11 | ||
12 | ||
13 | def get_package_info(module): | |
f6a765ce | 14 | return _Package( |
15 | name=getattr(module, '_yt_dlp__identifier', module.__name__), | |
16 | version=str(next(filter(None, ( | |
17 | getattr(module, attr, None) | |
18 | for attr in ('__version__', 'version_string', 'version') | |
19 | )), None))) | |
1d485a1a | 20 | |
21 | ||
9196cbfe | 22 | def _is_package(module): |
754c84e2 | 23 | return '__path__' in vars(module) |
24 | ||
25 | ||
88426d94 | 26 | def _is_dunder(name): |
27 | return name.startswith('__') and name.endswith('__') | |
28 | ||
29 | ||
754c84e2 | 30 | class EnhancedModule(types.ModuleType): |
754c84e2 | 31 | def __bool__(self): |
32 | return vars(self).get('__bool__', lambda: True)() | |
33 | ||
34 | def __getattribute__(self, attr): | |
35 | try: | |
36 | ret = super().__getattribute__(attr) | |
37 | except AttributeError: | |
88426d94 | 38 | if _is_dunder(attr): |
754c84e2 | 39 | raise |
40 | getter = getattr(self, '__getattr__', None) | |
41 | if not getter: | |
42 | raise | |
43 | ret = getter(attr) | |
44 | return ret.fget() if isinstance(ret, property) else ret | |
9196cbfe | 45 | |
46 | ||
88426d94 | 47 | def passthrough_module(parent, child, allowed_attributes=(..., ), *, callback=lambda _: None): |
754c84e2 | 48 | """Passthrough parent module into a child module, creating the parent if necessary""" |
754c84e2 | 49 | def __getattr__(attr): |
50 | if _is_package(parent): | |
65f6e807 | 51 | with contextlib.suppress(ModuleNotFoundError): |
754c84e2 | 52 | return importlib.import_module(f'.{attr}', parent.__name__) |
560738f3 | 53 | |
754c84e2 | 54 | ret = from_child(attr) |
55 | if ret is _NO_ATTRIBUTE: | |
56 | raise AttributeError(f'module {parent.__name__} has no attribute {attr}') | |
57 | callback(attr) | |
58 | return ret | |
9196cbfe | 59 | |
88426d94 | 60 | @functools.lru_cache(maxsize=None) |
754c84e2 | 61 | def from_child(attr): |
62 | nonlocal child | |
88426d94 | 63 | if attr not in allowed_attributes: |
64 | if ... not in allowed_attributes or _is_dunder(attr): | |
754c84e2 | 65 | return _NO_ATTRIBUTE |
9196cbfe | 66 | |
754c84e2 | 67 | if isinstance(child, str): |
68 | child = importlib.import_module(child, parent.__name__) | |
69 | ||
754c84e2 | 70 | if _is_package(child): |
71 | with contextlib.suppress(ImportError): | |
88426d94 | 72 | return passthrough_module(f'{parent.__name__}.{attr}', |
73 | importlib.import_module(f'.{attr}', child.__name__)) | |
74 | ||
75 | with contextlib.suppress(AttributeError): | |
76 | return getattr(child, attr) | |
754c84e2 | 77 | |
78 | return _NO_ATTRIBUTE | |
79 | ||
768a0017 | 80 | parent = sys.modules.get(parent, types.ModuleType(parent)) |
81 | parent.__class__ = EnhancedModule | |
754c84e2 | 82 | parent.__getattr__ = __getattr__ |
83 | return parent |