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