8 _NO_ATTRIBUTE
= object()
10 _Package
= collections
.namedtuple('Package', ('name', 'version'))
13 def get_package_info(module
):
15 name
=getattr(module
, '_yt_dlp__identifier', module
.__name
__),
16 version
=str(next(filter(None, (
17 getattr(module
, attr
, None)
18 for attr
in ('_yt_dlp__version', '__version__', 'version_string', 'version')
22 def _is_package(module
):
23 return '__path__' in vars(module
)
27 return name
.startswith('__') and name
.endswith('__')
30 class EnhancedModule(types
.ModuleType
):
32 return vars(self
).get('__bool__', lambda: True)()
34 def __getattribute__(self
, attr
):
36 ret
= super().__getattribute
__(attr
)
37 except AttributeError:
40 getter
= getattr(self
, '__getattr__', None)
44 return ret
.fget() if isinstance(ret
, property) else ret
47 def passthrough_module(parent
, child
, allowed_attributes
=(..., ), *, callback
=lambda _
: None):
48 """Passthrough parent module into a child module, creating the parent if necessary"""
49 def __getattr__(attr
):
50 if _is_package(parent
):
51 with contextlib
.suppress(ModuleNotFoundError
):
52 return importlib
.import_module(f
'.{attr}', parent
.__name
__)
54 ret
= from_child(attr
)
55 if ret
is _NO_ATTRIBUTE
:
56 raise AttributeError(f
'module {parent.__name__} has no attribute {attr}')
60 @functools.lru_cache(maxsize
=None)
63 if attr
not in allowed_attributes
:
64 if ... not in allowed_attributes
or _is_dunder(attr
):
67 if isinstance(child
, str):
68 child
= importlib
.import_module(child
, parent
.__name
__)
70 if _is_package(child
):
71 with contextlib
.suppress(ImportError):
72 return passthrough_module(f
'{parent.__name__}.{attr}',
73 importlib
.import_module(f
'.{attr}', child
.__name
__))
75 with contextlib
.suppress(AttributeError):
76 return getattr(child
, attr
)
80 parent
= sys
.modules
.get(parent
, types
.ModuleType(parent
))
81 parent
.__class
__ = EnhancedModule
82 parent
.__getattr
__ = __getattr__