mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-30 11:11:23 +01:00
Compare commits
2 Commits
69d3191495
...
f24e44e8cb
Author | SHA1 | Date | |
---|---|---|---|
|
f24e44e8cb | ||
|
811d298b23 |
|
@ -8,13 +8,9 @@ import pytest
|
||||||
|
|
||||||
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__))))
|
||||||
|
|
||||||
import contextlib
|
|
||||||
import io
|
import io
|
||||||
import platform
|
|
||||||
import random
|
import random
|
||||||
import ssl
|
import ssl
|
||||||
import urllib.error
|
|
||||||
import warnings
|
|
||||||
|
|
||||||
from yt_dlp.cookies import YoutubeDLCookieJar
|
from yt_dlp.cookies import YoutubeDLCookieJar
|
||||||
from yt_dlp.dependencies import certifi
|
from yt_dlp.dependencies import certifi
|
||||||
|
@ -30,7 +26,6 @@ from yt_dlp.networking._helper import (
|
||||||
from yt_dlp.networking.exceptions import (
|
from yt_dlp.networking.exceptions import (
|
||||||
HTTPError,
|
HTTPError,
|
||||||
IncompleteRead,
|
IncompleteRead,
|
||||||
_CompatHTTPError,
|
|
||||||
)
|
)
|
||||||
from yt_dlp.socks import ProxyType
|
from yt_dlp.socks import ProxyType
|
||||||
from yt_dlp.utils.networking import HTTPHeaderDict
|
from yt_dlp.utils.networking import HTTPHeaderDict
|
||||||
|
@ -179,11 +174,10 @@ class TestNetworkingExceptions:
|
||||||
def create_response(status):
|
def create_response(status):
|
||||||
return Response(fp=io.BytesIO(b'test'), url='http://example.com', headers={'tesT': 'test'}, status=status)
|
return Response(fp=io.BytesIO(b'test'), url='http://example.com', headers={'tesT': 'test'}, status=status)
|
||||||
|
|
||||||
@pytest.mark.parametrize('http_error_class', [HTTPError, lambda r: _CompatHTTPError(HTTPError(r))])
|
def test_http_error(self):
|
||||||
def test_http_error(self, http_error_class):
|
|
||||||
|
|
||||||
response = self.create_response(403)
|
response = self.create_response(403)
|
||||||
error = http_error_class(response)
|
error = HTTPError(response)
|
||||||
|
|
||||||
assert error.status == 403
|
assert error.status == 403
|
||||||
assert str(error) == error.msg == 'HTTP Error 403: Forbidden'
|
assert str(error) == error.msg == 'HTTP Error 403: Forbidden'
|
||||||
|
@ -194,80 +188,12 @@ class TestNetworkingExceptions:
|
||||||
assert data == b'test'
|
assert data == b'test'
|
||||||
assert repr(error) == '<HTTPError 403: Forbidden>'
|
assert repr(error) == '<HTTPError 403: Forbidden>'
|
||||||
|
|
||||||
@pytest.mark.parametrize('http_error_class', [HTTPError, lambda *args, **kwargs: _CompatHTTPError(HTTPError(*args, **kwargs))])
|
def test_redirect_http_error(self):
|
||||||
def test_redirect_http_error(self, http_error_class):
|
|
||||||
response = self.create_response(301)
|
response = self.create_response(301)
|
||||||
error = http_error_class(response, redirect_loop=True)
|
error = HTTPError(response, redirect_loop=True)
|
||||||
assert str(error) == error.msg == 'HTTP Error 301: Moved Permanently (redirect loop detected)'
|
assert str(error) == error.msg == 'HTTP Error 301: Moved Permanently (redirect loop detected)'
|
||||||
assert error.reason == 'Moved Permanently'
|
assert error.reason == 'Moved Permanently'
|
||||||
|
|
||||||
def test_compat_http_error(self):
|
|
||||||
response = self.create_response(403)
|
|
||||||
error = _CompatHTTPError(HTTPError(response))
|
|
||||||
assert isinstance(error, HTTPError)
|
|
||||||
assert isinstance(error, urllib.error.HTTPError)
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
|
||||||
def raises_deprecation_warning():
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
warnings.simplefilter('always')
|
|
||||||
yield
|
|
||||||
|
|
||||||
if len(w) == 0:
|
|
||||||
pytest.fail('Did not raise DeprecationWarning')
|
|
||||||
if len(w) > 1:
|
|
||||||
pytest.fail(f'Raised multiple warnings: {w}')
|
|
||||||
|
|
||||||
if not issubclass(w[-1].category, DeprecationWarning):
|
|
||||||
pytest.fail(f'Expected DeprecationWarning, got {w[-1].category}')
|
|
||||||
w.clear()
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.code == 403
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.getcode() == 403
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.hdrs is error.response.headers
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.info() is error.response.headers
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.headers is error.response.headers
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.filename == error.response.url
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.url == error.response.url
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.geturl() == error.response.url
|
|
||||||
|
|
||||||
# Passthrough file operations
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert error.read() == b'test'
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
assert not error.closed
|
|
||||||
|
|
||||||
with raises_deprecation_warning():
|
|
||||||
# Technically Response operations are also passed through, which should not be used.
|
|
||||||
assert error.get_header('test') == 'test'
|
|
||||||
|
|
||||||
# Should not raise a warning
|
|
||||||
error.close()
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
platform.python_implementation() == 'PyPy', reason='garbage collector works differently in pypy')
|
|
||||||
def test_compat_http_error_autoclose(self):
|
|
||||||
# Compat HTTPError should not autoclose response
|
|
||||||
response = self.create_response(403)
|
|
||||||
_CompatHTTPError(HTTPError(response))
|
|
||||||
assert not response.closed
|
|
||||||
|
|
||||||
def test_incomplete_read_error(self):
|
def test_incomplete_read_error(self):
|
||||||
error = IncompleteRead(4, 3, cause='test')
|
error = IncompleteRead(4, 3, cause='test')
|
||||||
assert isinstance(error, IncompleteRead)
|
assert isinstance(error, IncompleteRead)
|
||||||
|
|
|
@ -40,7 +40,6 @@ from .networking.exceptions import (
|
||||||
NoSupportingHandlers,
|
NoSupportingHandlers,
|
||||||
RequestError,
|
RequestError,
|
||||||
SSLError,
|
SSLError,
|
||||||
_CompatHTTPError,
|
|
||||||
network_exceptions,
|
network_exceptions,
|
||||||
)
|
)
|
||||||
from .plugins import directories as plugin_directories
|
from .plugins import directories as plugin_directories
|
||||||
|
@ -4110,8 +4109,6 @@ class YoutubeDL:
|
||||||
'SSLV3_ALERT_HANDSHAKE_FAILURE: The server may not support the current cipher list. '
|
'SSLV3_ALERT_HANDSHAKE_FAILURE: The server may not support the current cipher list. '
|
||||||
'Try using --legacy-server-connect', cause=e) from e
|
'Try using --legacy-server-connect', cause=e) from e
|
||||||
raise
|
raise
|
||||||
except HTTPError as e: # TODO: Remove in a future release
|
|
||||||
raise _CompatHTTPError(e) from e
|
|
||||||
|
|
||||||
def build_request_director(self, handlers, preferences=None):
|
def build_request_director(self, handlers, preferences=None):
|
||||||
logger = _YDLLogger(self)
|
logger = _YDLLogger(self)
|
||||||
|
|
|
@ -35,6 +35,7 @@ from .compat_utils import passthrough_module
|
||||||
from ..dependencies import brotli as compat_brotli # noqa: F401
|
from ..dependencies import brotli as compat_brotli # noqa: F401
|
||||||
from ..dependencies import websockets as compat_websockets # noqa: F401
|
from ..dependencies import websockets as compat_websockets # noqa: F401
|
||||||
from ..dependencies.Cryptodome import AES as compat_pycrypto_AES # noqa: F401
|
from ..dependencies.Cryptodome import AES as compat_pycrypto_AES # noqa: F401
|
||||||
|
from ..networking.exceptions import HTTPError as compat_HTTPError # noqa: F401
|
||||||
|
|
||||||
passthrough_module(__name__, '...utils', ('WINDOWS_VT_MODE', 'windows_enable_vt_mode'))
|
passthrough_module(__name__, '...utils', ('WINDOWS_VT_MODE', 'windows_enable_vt_mode'))
|
||||||
|
|
||||||
|
@ -70,7 +71,6 @@ compat_html_parser_HTMLParseError = compat_HTMLParseError
|
||||||
compat_HTMLParser = compat_html_parser_HTMLParser = html.parser.HTMLParser
|
compat_HTMLParser = compat_html_parser_HTMLParser = html.parser.HTMLParser
|
||||||
compat_http_client = http.client
|
compat_http_client = http.client
|
||||||
compat_http_server = http.server
|
compat_http_server = http.server
|
||||||
compat_HTTPError = urllib.error.HTTPError
|
|
||||||
compat_input = input
|
compat_input = input
|
||||||
compat_integer_types = (int, )
|
compat_integer_types = (int, )
|
||||||
compat_itertools_count = itertools.count
|
compat_itertools_count = itertools.count
|
||||||
|
@ -88,7 +88,7 @@ compat_struct_unpack = struct.unpack
|
||||||
compat_subprocess_get_DEVNULL = lambda: subprocess.DEVNULL
|
compat_subprocess_get_DEVNULL = lambda: subprocess.DEVNULL
|
||||||
compat_tokenize_tokenize = tokenize.tokenize
|
compat_tokenize_tokenize = tokenize.tokenize
|
||||||
compat_urllib_error = urllib.error
|
compat_urllib_error = urllib.error
|
||||||
compat_urllib_HTTPError = urllib.error.HTTPError
|
compat_urllib_HTTPError = compat_HTTPError
|
||||||
compat_urllib_parse = urllib.parse
|
compat_urllib_parse = urllib.parse
|
||||||
compat_urllib_parse_parse_qs = urllib.parse.parse_qs
|
compat_urllib_parse_parse_qs = urllib.parse.parse_qs
|
||||||
compat_urllib_parse_quote = urllib.parse.quote
|
compat_urllib_parse_quote = urllib.parse.quote
|
||||||
|
|
|
@ -369,6 +369,9 @@ class HlsFD(FragmentFD):
|
||||||
|
|
||||||
return output.getvalue().encode()
|
return output.getvalue().encode()
|
||||||
|
|
||||||
|
if len(fragments) == 1:
|
||||||
|
self.download_and_append_fragments(ctx, fragments, info_dict)
|
||||||
|
else:
|
||||||
self.download_and_append_fragments(
|
self.download_and_append_fragments(
|
||||||
ctx, fragments, info_dict, pack_func=pack_fragment, finish_func=fin_fragments)
|
ctx, fragments, info_dict, pack_func=pack_fragment, finish_func=fin_fragments)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import typing
|
import typing
|
||||||
import urllib.error
|
|
||||||
|
|
||||||
from ..utils import YoutubeDLError, deprecation_warning
|
from ..utils import YoutubeDLError
|
||||||
|
|
||||||
if typing.TYPE_CHECKING:
|
if typing.TYPE_CHECKING:
|
||||||
from .common import RequestHandler, Response
|
from .common import RequestHandler, Response
|
||||||
|
@ -101,117 +100,4 @@ class ProxyError(TransportError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class _CompatHTTPError(urllib.error.HTTPError, HTTPError):
|
|
||||||
"""
|
|
||||||
Provides backwards compatibility with urllib.error.HTTPError.
|
|
||||||
Do not use this class directly, use HTTPError instead.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, http_error: HTTPError):
|
|
||||||
super().__init__(
|
|
||||||
url=http_error.response.url,
|
|
||||||
code=http_error.status,
|
|
||||||
msg=http_error.msg,
|
|
||||||
hdrs=http_error.response.headers,
|
|
||||||
fp=http_error.response
|
|
||||||
)
|
|
||||||
self._closer.close_called = True # Disable auto close
|
|
||||||
self._http_error = http_error
|
|
||||||
HTTPError.__init__(self, http_error.response, redirect_loop=http_error.redirect_loop)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status(self):
|
|
||||||
return self._http_error.status
|
|
||||||
|
|
||||||
@status.setter
|
|
||||||
def status(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def reason(self):
|
|
||||||
return self._http_error.reason
|
|
||||||
|
|
||||||
@reason.setter
|
|
||||||
def reason(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def headers(self):
|
|
||||||
deprecation_warning('HTTPError.headers is deprecated, use HTTPError.response.headers instead')
|
|
||||||
return self._http_error.response.headers
|
|
||||||
|
|
||||||
@headers.setter
|
|
||||||
def headers(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
def info(self):
|
|
||||||
deprecation_warning('HTTPError.info() is deprecated, use HTTPError.response.headers instead')
|
|
||||||
return self.response.headers
|
|
||||||
|
|
||||||
def getcode(self):
|
|
||||||
deprecation_warning('HTTPError.getcode is deprecated, use HTTPError.status instead')
|
|
||||||
return self.status
|
|
||||||
|
|
||||||
def geturl(self):
|
|
||||||
deprecation_warning('HTTPError.geturl is deprecated, use HTTPError.response.url instead')
|
|
||||||
return self.response.url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def code(self):
|
|
||||||
deprecation_warning('HTTPError.code is deprecated, use HTTPError.status instead')
|
|
||||||
return self.status
|
|
||||||
|
|
||||||
@code.setter
|
|
||||||
def code(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def url(self):
|
|
||||||
deprecation_warning('HTTPError.url is deprecated, use HTTPError.response.url instead')
|
|
||||||
return self.response.url
|
|
||||||
|
|
||||||
@url.setter
|
|
||||||
def url(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def hdrs(self):
|
|
||||||
deprecation_warning('HTTPError.hdrs is deprecated, use HTTPError.response.headers instead')
|
|
||||||
return self.response.headers
|
|
||||||
|
|
||||||
@hdrs.setter
|
|
||||||
def hdrs(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
@property
|
|
||||||
def filename(self):
|
|
||||||
deprecation_warning('HTTPError.filename is deprecated, use HTTPError.response.url instead')
|
|
||||||
return self.response.url
|
|
||||||
|
|
||||||
@filename.setter
|
|
||||||
def filename(self, value):
|
|
||||||
return
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
# File operations are passed through the response.
|
|
||||||
# Warn for some commonly used ones
|
|
||||||
passthrough_warnings = {
|
|
||||||
'read': 'response.read()',
|
|
||||||
# technically possibly due to passthrough, but we should discourage this
|
|
||||||
'get_header': 'response.get_header()',
|
|
||||||
'readable': 'response.readable()',
|
|
||||||
'closed': 'response.closed',
|
|
||||||
'tell': 'response.tell()',
|
|
||||||
}
|
|
||||||
if name in passthrough_warnings:
|
|
||||||
deprecation_warning(f'HTTPError.{name} is deprecated, use HTTPError.{passthrough_warnings[name]} instead')
|
|
||||||
return super().__getattr__(name)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return str(self._http_error)
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return repr(self._http_error)
|
|
||||||
|
|
||||||
|
|
||||||
network_exceptions = (HTTPError, TransportError)
|
network_exceptions = (HTTPError, TransportError)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user