]> jfr.im git - yt-dlp.git/blame - yt_dlp/compat/compat_utils.py
[ie/PatreonCampaign] Fix `campaign_id` extraction (#10070)
[yt-dlp.git] / yt_dlp / compat / compat_utils.py
CommitLineData
1d485a1a 1import collections
9196cbfe 2import contextlib
88426d94 3import functools
9196cbfe 4import importlib
5import sys
6import types
7
1d485a1a 8_NO_ATTRIBUTE = object()
9
10_Package = collections.namedtuple('Package', ('name', 'version'))
11
12
13def 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)
35f9a306 18 for attr in ('_yt_dlp__version', '__version__', 'version_string', 'version')
f6a765ce 19 )), None)))
1d485a1a 20
21
9196cbfe 22def _is_package(module):
754c84e2 23 return '__path__' in vars(module)
24
25
88426d94 26def _is_dunder(name):
27 return name.startswith('__') and name.endswith('__')
28
29
754c84e2 30class 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 47def 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