]> jfr.im git - yt-dlp.git/blob - yt_dlp/compat/compat_utils.py
[cleanup] Consistent style for file heads
[yt-dlp.git] / yt_dlp / compat / compat_utils.py
1 import collections
2 import contextlib
3 import importlib
4 import sys
5 import types
6
7 _NO_ATTRIBUTE = object()
8
9 _Package = collections.namedtuple('Package', ('name', 'version'))
10
11
12 def get_package_info(module):
13 parent = module.__name__.split('.')[0]
14 parent_module = None
15 with contextlib.suppress(ImportError):
16 parent_module = importlib.import_module(parent)
17
18 for attr in ('__version__', 'version_string', 'version'):
19 version = getattr(parent_module, attr, None)
20 if version is not None:
21 break
22 return _Package(getattr(module, '_yt_dlp__identifier', parent), str(version))
23
24
25 def _is_package(module):
26 try:
27 module.__getattribute__('__path__')
28 except AttributeError:
29 return False
30 return True
31
32
33 def passthrough_module(parent, child, allowed_attributes=None, *, callback=lambda _: None):
34 parent_module = importlib.import_module(parent)
35 child_module = None # Import child module only as needed
36
37 class PassthroughModule(types.ModuleType):
38 def __getattr__(self, attr):
39 if _is_package(parent_module):
40 with contextlib.suppress(ImportError):
41 return importlib.import_module(f'.{attr}', parent)
42
43 ret = self.__from_child(attr)
44 if ret is _NO_ATTRIBUTE:
45 raise AttributeError(f'module {parent} has no attribute {attr}')
46 callback(attr)
47 return ret
48
49 def __from_child(self, attr):
50 if allowed_attributes is None:
51 if attr.startswith('__') and attr.endswith('__'):
52 return _NO_ATTRIBUTE
53 elif attr not in allowed_attributes:
54 return _NO_ATTRIBUTE
55
56 nonlocal child_module
57 child_module = child_module or importlib.import_module(child, parent)
58
59 with contextlib.suppress(AttributeError):
60 return getattr(child_module, attr)
61
62 if _is_package(child_module):
63 with contextlib.suppress(ImportError):
64 return importlib.import_module(f'.{attr}', child)
65
66 return _NO_ATTRIBUTE
67
68 # Python 3.6 does not have module level __getattr__
69 # https://peps.python.org/pep-0562/
70 sys.modules[parent].__class__ = PassthroughModule