diff --git a/Makefile b/Makefile index ca7d641ab8..d5d47629b9 100644 --- a/Makefile +++ b/Makefile @@ -74,7 +74,7 @@ offlinetest: codetest $(PYTHON) -m pytest -k "not download" # XXX: This is hard to maintain -CODE_FOLDERS = yt_dlp yt_dlp/downloader yt_dlp/extractor yt_dlp/postprocessor yt_dlp/compat +CODE_FOLDERS = yt_dlp yt_dlp/downloader yt_dlp/extractor yt_dlp/postprocessor yt_dlp/compat yt_dlp/dependencies yt-dlp: yt_dlp/*.py yt_dlp/*/*.py mkdir -p zip for d in $(CODE_FOLDERS) ; do \ diff --git a/yt_dlp/compat/compat_utils.py b/yt_dlp/compat/compat_utils.py index 373389a466..f8679c98ec 100644 --- a/yt_dlp/compat/compat_utils.py +++ b/yt_dlp/compat/compat_utils.py @@ -1,5 +1,6 @@ import collections import contextlib +import functools import importlib import sys import types @@ -22,6 +23,10 @@ def _is_package(module): return '__path__' in vars(module) +def _is_dunder(name): + return name.startswith('__') and name.endswith('__') + + class EnhancedModule(types.ModuleType): def __new__(cls, name, *args, **kwargs): if name not in sys.modules: @@ -44,7 +49,7 @@ def __getattribute__(self, attr): try: ret = super().__getattribute__(attr) except AttributeError: - if attr.startswith('__') and attr.endswith('__'): + if _is_dunder(attr): raise getter = getattr(self, '__getattr__', None) if not getter: @@ -53,7 +58,7 @@ def __getattribute__(self, attr): return ret.fget() if isinstance(ret, property) else ret -def passthrough_module(parent, child, allowed_attributes=None, *, callback=lambda _: None): +def passthrough_module(parent, child, allowed_attributes=(..., ), *, callback=lambda _: None): """Passthrough parent module into a child module, creating the parent if necessary""" parent = EnhancedModule(parent) @@ -68,24 +73,23 @@ def __getattr__(attr): callback(attr) return ret + @functools.lru_cache(maxsize=None) def from_child(attr): nonlocal child - - if allowed_attributes is None: - if attr.startswith('__') and attr.endswith('__'): + if attr not in allowed_attributes: + if ... not in allowed_attributes or _is_dunder(attr): return _NO_ATTRIBUTE - elif attr not in allowed_attributes: - return _NO_ATTRIBUTE if isinstance(child, str): child = importlib.import_module(child, parent.__name__) - with contextlib.suppress(AttributeError): - return getattr(child, attr) - if _is_package(child): with contextlib.suppress(ImportError): - return importlib.import_module(f'.{attr}', child.__name__) + return passthrough_module(f'{parent.__name__}.{attr}', + importlib.import_module(f'.{attr}', child.__name__)) + + with contextlib.suppress(AttributeError): + return getattr(child, attr) return _NO_ATTRIBUTE diff --git a/yt_dlp/dependencies/Cryptodome.py b/yt_dlp/dependencies/Cryptodome.py index b95f45d720..580ce07533 100644 --- a/yt_dlp/dependencies/Cryptodome.py +++ b/yt_dlp/dependencies/Cryptodome.py @@ -1,10 +1,6 @@ -import importlib - from ..compat import functools from ..compat.compat_utils import EnhancedModule, passthrough_module -EnhancedModule(__name__) - try: import Cryptodome as _parent except ImportError: @@ -14,14 +10,8 @@ _parent = EnhancedModule('Cryptodome') __bool__ = lambda: False - -@functools.cache -def __getattr__(name): - try: - submodule = importlib.import_module(f'.{name}', _parent.__name__) - except ImportError: - return getattr(_parent, name) - return passthrough_module(f'{__name__}.{name}', submodule) +passthrough_module(__name__, _parent, (..., '__version__')) +del passthrough_module, EnhancedModule @property