Compare commits

...

3 Commits

Author SHA1 Message Date
pukkandan
059bc4db19
[compat/asyncio] Use asyncio.all_tasks 2022-04-26 05:45:18 +05:30
pukkandan
9196cbfe8b
[compat] Ensure submodules are correctly wrapped 2022-04-26 05:43:20 +05:30
pukkandan
9cd080508d
Revert acbc642250
Reverts "[utils] WebSocketsWrapper: Ignore warnings at websockets instantiation"

The warning should not be suppressed. We need to address it
2022-04-26 05:43:19 +05:30
8 changed files with 82 additions and 45 deletions

View File

@ -66,7 +66,7 @@ offlinetest: codetest
# XXX: This is hard to maintain # 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/compat/asyncio yt_dlp/extractor/anvato_token_generator yt_dlp/extractor/anvato_token_generator
yt-dlp: yt_dlp/*.py yt_dlp/*/*.py yt-dlp: yt_dlp/*.py yt_dlp/*/*.py
mkdir -p zip mkdir -p zip
for d in $(CODE_FOLDERS) ; do \ for d in $(CODE_FOLDERS) ; do \

View File

@ -7,6 +7,7 @@ import unittest
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from yt_dlp import compat
from yt_dlp.compat import ( from yt_dlp.compat import (
compat_etree_fromstring, compat_etree_fromstring,
compat_expanduser, compat_expanduser,
@ -21,6 +22,12 @@ from yt_dlp.compat import (
class TestCompat(unittest.TestCase): 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): def test_compat_getenv(self):
test_str = 'тест' test_str = 'тест'
compat_setenv('yt_dlp_COMPAT_GETENV', test_str) compat_setenv('yt_dlp_COMPAT_GETENV', test_str)

View File

@ -2,11 +2,18 @@ import contextlib
import os import os
import subprocess import subprocess
import sys import sys
import types import warnings
import xml.etree.ElementTree as etree import xml.etree.ElementTree as etree
from . import re from . import re
from ._deprecated import * # noqa: F401, F403 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 # 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): with contextlib.suppress(Exception):
subprocess.Popen('', shell=True, startupinfo=startupinfo).wait() subprocess.Popen('', shell=True, startupinfo=startupinfo).wait()
WINDOWS_VT_MODE = True 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

View File

@ -2,7 +2,10 @@
from asyncio import * # noqa: F403 from asyncio import * # noqa: F403
from . import tasks # noqa: F401 from .compat_utils import passthrough_module
passthrough_module(__name__, 'asyncio')
del passthrough_module
try: try:
run # >= 3.7 run # >= 3.7
@ -14,3 +17,8 @@ except NameError:
loop = new_event_loop() loop = new_event_loop()
set_event_loop(loop) set_event_loop(loop)
loop.run_until_complete(coro) loop.run_until_complete(coro)
try:
all_tasks # >= 3.7
except NameError:
all_tasks = Task.all_tasks

View File

@ -1,8 +0,0 @@
# flake8: noqa: F405
from asyncio.tasks import * # noqa: F403
try: # >= 3.7
all_tasks
except NameError:
all_tasks = Task.all_tasks

View File

@ -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

View File

@ -2,6 +2,11 @@
from re import * # F403 from re import * # F403
from .compat_utils import passthrough_module
passthrough_module(__name__, 're')
del passthrough_module
try: try:
Pattern # >= 3.7 Pattern # >= 3.7
except NameError: except NameError:

View File

@ -36,7 +36,6 @@ import tempfile
import time import time
import traceback import traceback
import urllib.parse import urllib.parse
import warnings
import xml.etree.ElementTree import xml.etree.ElementTree
import zlib import zlib
@ -5223,22 +5222,17 @@ class WebSocketsWrapper():
def __init__(self, url, headers=None, connect=True): def __init__(self, url, headers=None, connect=True):
self.loop = asyncio.new_event_loop() self.loop = asyncio.new_event_loop()
with warnings.catch_warnings(): # XXX: "loop" is deprecated
warnings.simplefilter("ignore") self.conn = websockets.connect(
# https://github.com/aaugustin/websockets/blob/9c87d43f1d7bbf6847350087aae74fd35f73a642/src/websockets/legacy/client.py#L480 url, extra_headers=headers, ping_interval=None,
# the reason to keep giving `loop` parameter: we aren't in async function close_timeout=float('inf'), loop=self.loop, ping_timeout=float('inf'))
self.conn = websockets.connect(
url, extra_headers=headers, ping_interval=None,
close_timeout=float('inf'), loop=self.loop, ping_timeout=float('inf'))
if connect: if connect:
self.__enter__() self.__enter__()
atexit.register(self.__exit__, None, None, None) atexit.register(self.__exit__, None, None, None)
def __enter__(self): def __enter__(self):
if not self.pool: if not self.pool:
with warnings.catch_warnings(): self.pool = self.run_with_loop(self.conn.__aenter__(), self.loop)
warnings.simplefilter("ignore")
self.pool = self.run_with_loop(self.conn.__aenter__(), self.loop)
return self return self
def send(self, *args): def send(self, *args):
@ -5270,7 +5264,7 @@ class WebSocketsWrapper():
@staticmethod @staticmethod
def _cancel_all_tasks(loop): def _cancel_all_tasks(loop):
to_cancel = asyncio.tasks.all_tasks(loop) to_cancel = asyncio.all_tasks(loop)
if not to_cancel: if not to_cancel:
return return
@ -5278,8 +5272,9 @@ class WebSocketsWrapper():
for task in to_cancel: for task in to_cancel:
task.cancel() task.cancel()
# XXX: "loop" is removed in python 3.10+
loop.run_until_complete( loop.run_until_complete(
asyncio.tasks.gather(*to_cancel, loop=loop, return_exceptions=True)) asyncio.gather(*to_cancel, loop=loop, return_exceptions=True))
for task in to_cancel: for task in to_cancel:
if task.cancelled(): if task.cancelled():