]> jfr.im git - yt-dlp.git/commitdiff
[compat] Ensure submodules are correctly wrapped
authorpukkandan <redacted>
Sun, 24 Apr 2022 16:28:18 +0000 (21:58 +0530)
committerpukkandan <redacted>
Tue, 26 Apr 2022 00:13:20 +0000 (05:43 +0530)
test/test_compat.py
yt_dlp/compat/__init__.py
yt_dlp/compat/asyncio/__init__.py
yt_dlp/compat/asyncio/tasks.py
yt_dlp/compat/compat_utils.py [new file with mode: 0644]
yt_dlp/compat/re.py

index 8e40a418027e9c6486ae0425c753b9da77c69066..9b185853da9ff6ee49856ff41e42cbc707c094d1 100644 (file)
@@ -7,6 +7,7 @@
 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)
index 56a65bb6ce7b38cc2630a33842eeb74651b24252..3c395f6d9689356a70957eabd4403867b744f019 100644 (file)
@@ -2,11 +2,18 @@
 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
@@ -85,24 +92,3 @@ def windows_enable_vt_mode():  # TODO: Do this the proper way https://bugs.pytho
     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
index 0e8c6cad3f0acdb9764172ef652711193411cc06..21b494499a526eeff089c15a28a3dac93694f842 100644 (file)
@@ -3,6 +3,10 @@
 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
index cb31e52fabbbe28697523170772c473072db0c1a..9d98fdfeb67c190fd92d7fed3df5ca9335e8c938 100644 (file)
@@ -2,6 +2,11 @@
 
 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:
diff --git a/yt_dlp/compat/compat_utils.py b/yt_dlp/compat/compat_utils.py
new file mode 100644 (file)
index 0000000..938daf9
--- /dev/null
@@ -0,0 +1,44 @@
+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
index e8a6fabbda6701529c971a23f4019073506a60f3..d4532950a4dc90487fbdfe604e28bb3a616a3bbe 100644 (file)
@@ -2,6 +2,11 @@
 
 from re import *  # F403
 
+from .compat_utils import passthrough_module
+
+passthrough_module(__name__, 're')
+del passthrough_module
+
 try:
     Pattern  # >= 3.7
 except NameError: