compat_utils.py 1.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
  1. import collections
  2. import contextlib
  3. import importlib
  4. import sys
  5. import types
  6. _NO_ATTRIBUTE = object()
  7. _Package = collections.namedtuple('Package', ('name', 'version'))
  8. def get_package_info(module):
  9. parent = module.__name__.split('.')[0]
  10. parent_module = None
  11. with contextlib.suppress(ImportError):
  12. parent_module = importlib.import_module(parent)
  13. for attr in ('__version__', 'version_string', 'version'):
  14. version = getattr(parent_module, attr, None)
  15. if version is not None:
  16. break
  17. return _Package(getattr(module, '_yt_dlp__identifier', parent), str(version))
  18. def _is_package(module):
  19. try:
  20. module.__getattribute__('__path__')
  21. except AttributeError:
  22. return False
  23. return True
  24. def passthrough_module(parent, child, *, callback=lambda _: None):
  25. parent_module = importlib.import_module(parent)
  26. child_module = None # Import child module only as needed
  27. class PassthroughModule(types.ModuleType):
  28. def __getattr__(self, attr):
  29. if _is_package(parent_module):
  30. with contextlib.suppress(ImportError):
  31. return importlib.import_module(f'.{attr}', parent)
  32. nonlocal child_module
  33. child_module = child_module or importlib.import_module(child, parent)
  34. ret = _NO_ATTRIBUTE
  35. with contextlib.suppress(AttributeError):
  36. ret = getattr(child_module, attr)
  37. if _is_package(child_module):
  38. with contextlib.suppress(ImportError):
  39. ret = importlib.import_module(f'.{attr}', child)
  40. if ret is _NO_ATTRIBUTE:
  41. raise AttributeError(f'module {parent} has no attribute {attr}')
  42. callback(attr)
  43. return ret
  44. # Python 3.6 does not have module level __getattr__
  45. # https://peps.python.org/pep-0562/
  46. sys.modules[parent].__class__ = PassthroughModule