sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from yt_dlp import compat
from yt_dlp.compat import (
compat_etree_fromstring,
compat_expanduser,
class TestCompat(unittest.TestCase):
+ def test_compat_passthrough(self):
+ with self.assertWarns(DeprecationWarning):
+ compat.compat_basestring
+
+ compat.asyncio.events # Must not raise error
+
def test_compat_getenv(self):
test_str = 'ัะตัั'
compat_setenv('yt_dlp_COMPAT_GETENV', test_str)
import os
import subprocess
import sys
-import types
+import warnings
import xml.etree.ElementTree as etree
from . import re
from ._deprecated import * # noqa: F401, F403
+from .compat_utils import passthrough_module
+
+
+# XXX: Implement this the same way as other DeprecationWarnings without circular import
+passthrough_module(__name__, '._legacy', callback=lambda attr: warnings.warn(
+ DeprecationWarning(f'{__name__}.{attr} is deprecated'), stacklevel=2))
+del passthrough_module
# HTMLParseError has been deprecated in Python 3.3 and removed in
with contextlib.suppress(Exception):
subprocess.Popen('', shell=True, startupinfo=startupinfo).wait()
WINDOWS_VT_MODE = True
-
-
-class _PassthroughLegacy(types.ModuleType):
- def __getattr__(self, attr):
- import importlib
- with contextlib.suppress(ImportError):
- return importlib.import_module(f'.{attr}', __name__)
-
- legacy = importlib.import_module('._legacy', __name__)
- if not hasattr(legacy, attr):
- raise AttributeError(f'module {__name__} has no attribute {attr}')
-
- # XXX: Implement this the same way as other DeprecationWarnings without circular import
- import warnings
- warnings.warn(DeprecationWarning(f'{__name__}.{attr} is deprecated'), stacklevel=2)
- return getattr(legacy, attr)
-
-
-# Python 3.6 does not have module level __getattr__
-# https://peps.python.org/pep-0562/
-sys.modules[__name__].__class__ = _PassthroughLegacy
from asyncio import * # noqa: F403
from . import tasks # noqa: F401
+from ..compat_utils import passthrough_module
+
+passthrough_module(__name__, 'asyncio')
+del passthrough_module
try:
run # >= 3.7
from asyncio.tasks import * # noqa: F403
+from ..compat_utils import passthrough_module
+
+passthrough_module(__name__, 'asyncio.tasks')
+del passthrough_module
+
try: # >= 3.7
all_tasks
except NameError:
--- /dev/null
+import contextlib
+import importlib
+import sys
+import types
+
+
+def _is_package(module):
+ try:
+ module.__getattribute__('__path__')
+ except AttributeError:
+ return False
+ return True
+
+
+_NO_ATTRIBUTE = object()
+
+
+def passthrough_module(parent, child, *, callback=lambda _: None):
+ parent_module = importlib.import_module(parent)
+ child_module = importlib.import_module(child, parent)
+
+ class PassthroughModule(types.ModuleType):
+ def __getattr__(self, attr):
+ if _is_package(parent_module):
+ with contextlib.suppress(ImportError):
+ return importlib.import_module(f'.{attr}', parent)
+
+ ret = _NO_ATTRIBUTE
+ with contextlib.suppress(AttributeError):
+ ret = getattr(child_module, attr)
+
+ if _is_package(child_module):
+ with contextlib.suppress(ImportError):
+ ret = importlib.import_module(f'.{attr}', child)
+
+ if ret is _NO_ATTRIBUTE:
+ raise AttributeError(f'module {parent} has no attribute {attr}')
+
+ callback(attr)
+ return ret
+
+ # Python 3.6 does not have module level __getattr__
+ # https://peps.python.org/pep-0562/
+ sys.modules[parent].__class__ = PassthroughModule
from re import * # F403
+from .compat_utils import passthrough_module
+
+passthrough_module(__name__, 're')
+del passthrough_module
+
try:
Pattern # >= 3.7
except NameError: