[cleanup] Remove obsolete Python compatibility code (#17357)
Authored by: doe1080
This commit is contained in:
parent
81ecd58b13
commit
88a9516584
17 changed files with 43 additions and 118 deletions
|
|
@ -66,8 +66,7 @@ def convert_code_blocks(readme):
|
|||
|
||||
def move_sections(readme):
|
||||
MOVE_TAG_TEMPLATE = '<!-- MANPAGE: MOVE "%s" SECTION HERE -->'
|
||||
sections = re.findall(r'(?m)^%s$' % (
|
||||
re.escape(MOVE_TAG_TEMPLATE).replace(r'\%', '%') % '(.+)'), readme)
|
||||
sections = re.findall(r'(?m)^%s$' % (re.escape(MOVE_TAG_TEMPLATE) % '(.+)'), readme)
|
||||
|
||||
for section_name in sections:
|
||||
move_tag = MOVE_TAG_TEMPLATE % section_name
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import json
|
||||
import os.path
|
||||
import re
|
||||
import ssl
|
||||
import sys
|
||||
import types
|
||||
|
||||
|
|
@ -318,36 +317,6 @@ def _repr(v):
|
|||
'Missing keys in test definition: {}'.format(', '.join(sorted(missing_keys))))
|
||||
|
||||
|
||||
def assertRegexpMatches(self, text, regexp, msg=None):
|
||||
if hasattr(self, 'assertRegexp'):
|
||||
return self.assertRegexp(text, regexp, msg)
|
||||
else:
|
||||
m = re.match(regexp, text)
|
||||
if not m:
|
||||
note = f'Regexp didn\'t match: {regexp!r} not found'
|
||||
if len(text) < 1000:
|
||||
note += f' in {text!r}'
|
||||
if msg is None:
|
||||
msg = note
|
||||
else:
|
||||
msg = note + ', ' + msg
|
||||
self.assertTrue(m, msg)
|
||||
|
||||
|
||||
def assertGreaterEqual(self, got, expected, msg=None):
|
||||
if not (got >= expected):
|
||||
if msg is None:
|
||||
msg = f'{got!r} not greater than or equal to {expected!r}'
|
||||
self.assertTrue(got >= expected, msg)
|
||||
|
||||
|
||||
def assertLessEqual(self, got, expected, msg=None):
|
||||
if not (got <= expected):
|
||||
if msg is None:
|
||||
msg = f'{got!r} not less than or equal to {expected!r}'
|
||||
self.assertTrue(got <= expected, msg)
|
||||
|
||||
|
||||
def assertEqual(self, got, expected, msg=None):
|
||||
if got != expected:
|
||||
if msg is None:
|
||||
|
|
@ -366,12 +335,7 @@ def _report_warning(w, *args, **kwargs):
|
|||
|
||||
|
||||
def http_server_port(httpd):
|
||||
if os.name == 'java' and isinstance(httpd.socket, ssl.SSLSocket):
|
||||
# In Jython SSLSocket is not a subclass of socket.socket
|
||||
sock = httpd.socket.sock
|
||||
else:
|
||||
sock = httpd.socket
|
||||
return sock.getsockname()[1]
|
||||
return httpd.server_address[1]
|
||||
|
||||
|
||||
def verify_address_availability(address):
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
import copy
|
||||
import json
|
||||
|
||||
from test.helper import FakeYDL, assertRegexpMatches, try_rm
|
||||
from test.helper import FakeYDL, try_rm
|
||||
from yt_dlp import YoutubeDL
|
||||
from yt_dlp.extractor.common import InfoExtractor
|
||||
from yt_dlp.postprocessor.common import PostProcessor
|
||||
|
|
@ -860,10 +860,10 @@ def gen():
|
|||
def test_format_note(self):
|
||||
ydl = YoutubeDL()
|
||||
self.assertEqual(ydl._format_note({}), '')
|
||||
assertRegexpMatches(self, ydl._format_note({
|
||||
self.assertRegex(ydl._format_note({
|
||||
'vbr': 10,
|
||||
}), r'^\s*10k$')
|
||||
assertRegexpMatches(self, ydl._format_note({
|
||||
self.assertRegex(ydl._format_note({
|
||||
'fps': 30,
|
||||
}), r'^30fps$')
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
import json
|
||||
|
||||
from test.helper import (
|
||||
assertGreaterEqual,
|
||||
assertLessEqual,
|
||||
expect_info_dict,
|
||||
expect_warnings,
|
||||
get_params,
|
||||
|
|
@ -201,8 +199,8 @@ def try_rm_tcs_files(tcs=None):
|
|||
num_entries = len(res_dict.get('entries', []))
|
||||
if 'playlist_mincount' in test_case:
|
||||
mincount = test_case['playlist_mincount']
|
||||
assertGreaterEqual(
|
||||
self, num_entries, mincount,
|
||||
self.assertGreaterEqual(
|
||||
num_entries, mincount,
|
||||
f'Expected at least {mincount} entries in playlist {test_url}, but got only {num_entries}')
|
||||
if 'playlist_count' in test_case:
|
||||
count = test_case['playlist_count']
|
||||
|
|
@ -212,8 +210,8 @@ def try_rm_tcs_files(tcs=None):
|
|||
f'Expected exactly {count} entries in playlist {test_url}, but got {got}')
|
||||
if 'playlist_maxcount' in test_case:
|
||||
maxcount = test_case['playlist_maxcount']
|
||||
assertLessEqual(
|
||||
self, num_entries, maxcount,
|
||||
self.assertLessEqual(
|
||||
num_entries, maxcount,
|
||||
f'Expected at most {maxcount} entries in playlist {test_url}, but got more')
|
||||
if 'playlist_duration_sum' in test_case:
|
||||
got_duration = sum(e['duration'] for e in res_dict['entries'])
|
||||
|
|
@ -241,8 +239,8 @@ def try_rm_tcs_files(tcs=None):
|
|||
if params.get('test'):
|
||||
expected_minsize = max(expected_minsize, 10000)
|
||||
got_fsize = os.path.getsize(tc_filename)
|
||||
assertGreaterEqual(
|
||||
self, got_fsize, expected_minsize,
|
||||
self.assertGreaterEqual(
|
||||
got_fsize, expected_minsize,
|
||||
f'Expected {tc_filename} to be at least {format_bytes(expected_minsize)}, '
|
||||
f'but it\'s only {format_bytes(got_fsize)} ')
|
||||
if 'md5' in tc:
|
||||
|
|
|
|||
|
|
@ -984,28 +984,15 @@ def test_verify_cert_error_text(self, handler):
|
|||
):
|
||||
validate_and_send(rh, Request(f'https://127.0.0.1:{self.https_port}/headers'))
|
||||
|
||||
@pytest.mark.parametrize('req,match,version_check', [
|
||||
@pytest.mark.parametrize('req,match', [
|
||||
# https://github.com/python/cpython/blob/987b712b4aeeece336eed24fcc87a950a756c3e2/Lib/http/client.py#L1256
|
||||
# bpo-39603: Check implemented in 3.7.9+, 3.8.5+
|
||||
(
|
||||
Request('http://127.0.0.1', method='GET\n'),
|
||||
'method can\'t contain control characters',
|
||||
lambda v: v < (3, 7, 9) or (3, 8, 0) <= v < (3, 8, 5),
|
||||
),
|
||||
(Request('http://127.0.0.1', method='GET\n'), 'method can\'t contain control characters'),
|
||||
# https://github.com/python/cpython/blob/987b712b4aeeece336eed24fcc87a950a756c3e2/Lib/http/client.py#L1265
|
||||
# bpo-38576: Check implemented in 3.7.8+, 3.8.3+
|
||||
(
|
||||
Request('http://127.0.0. 1', method='GET'),
|
||||
'URL can\'t contain control characters',
|
||||
lambda v: v < (3, 7, 8) or (3, 8, 0) <= v < (3, 8, 3),
|
||||
),
|
||||
(Request('http://127.0.0. 1', method='GET'), 'URL can\'t contain control characters'),
|
||||
# https://github.com/python/cpython/blob/987b712b4aeeece336eed24fcc87a950a756c3e2/Lib/http/client.py#L1288C31-L1288C50
|
||||
(Request('http://127.0.0.1', headers={'foo\n': 'bar'}), 'Invalid header name', None),
|
||||
(Request('http://127.0.0.1', headers={'foo\n': 'bar'}), 'Invalid header name'),
|
||||
])
|
||||
def test_httplib_validation_errors(self, handler, req, match, version_check):
|
||||
if version_check and version_check(sys.version_info):
|
||||
pytest.skip(f'Python {sys.version} version does not have the required validation for this test.')
|
||||
|
||||
def test_httplib_validation_errors(self, handler, req, match):
|
||||
with handler() as rh:
|
||||
with pytest.raises(RequestError, match=match) as exc_info:
|
||||
validate_and_send(rh, req)
|
||||
|
|
|
|||
|
|
@ -1347,15 +1347,8 @@ def test_extract_attributes(self):
|
|||
self.assertEqual(extract_attributes('<e _:funny-name1=1>'), {'_:funny-name1': '1'})
|
||||
self.assertEqual(extract_attributes('<e x="Fáilte 世界 \U0001f600">'), {'x': 'Fáilte 世界 \U0001f600'})
|
||||
self.assertEqual(extract_attributes('<e x="décomposé">'), {'x': 'décompose\u0301'})
|
||||
# "Narrow" Python builds don't support unicode code points outside BMP.
|
||||
try:
|
||||
chr(0x10000)
|
||||
supports_outside_bmp = True
|
||||
except ValueError:
|
||||
supports_outside_bmp = False
|
||||
if supports_outside_bmp:
|
||||
self.assertEqual(extract_attributes('<e x="Smile 😀!">'), {'x': 'Smile \U0001f600!'})
|
||||
# Malformed HTML should not break attributes extraction on older Python
|
||||
self.assertEqual(extract_attributes('<e x="Smile 😀!">'), {'x': 'Smile \U0001f600!'})
|
||||
# Malformed HTML should not break attribute extraction
|
||||
self.assertEqual(extract_attributes('<mal"formed/>'), {})
|
||||
|
||||
def test_clean_html(self):
|
||||
|
|
|
|||
|
|
@ -2398,8 +2398,7 @@ def _parse_format_selection(tokens, inside_merge=False, inside_choice=False, ins
|
|||
selectors = []
|
||||
current_selector = None
|
||||
for type_, string_, start, _, _ in tokens:
|
||||
# ENCODING is only defined in Python 3.x
|
||||
if type_ == getattr(tokenize, 'ENCODING', None):
|
||||
if type_ == tokenize.ENCODING:
|
||||
continue
|
||||
elif type_ in [tokenize.NAME, tokenize.NUMBER]:
|
||||
current_selector = FormatSelector(SINGLE, string_, [])
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ def aes_decrypt_text(data, password, key_size_bytes):
|
|||
- Mode of operation is 'counter'
|
||||
|
||||
@param {str} data Base64 encoded string
|
||||
@param {str,unicode} password Password (will be encoded with utf-8)
|
||||
@param {str} password Password (will be encoded with UTF-8)
|
||||
@param {int} key_size_bytes Possible values: 16 for 128-Bit, 24 for 192-Bit or 32 for 256-Bit
|
||||
@returns {str} Decrypted data
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
del passthrough_module
|
||||
|
||||
|
||||
# HTMLParseError has been deprecated in Python 3.3 and removed in
|
||||
# Python 3.5. Introducing dummy exception for Python >3.5 for compatible
|
||||
# and uniform cross-version exception handling
|
||||
# HTMLParseError was deprecated in Python 3.3 and removed in Python 3.5.
|
||||
# Keep a replacement for API compatibility and uniform exception handling.
|
||||
class compat_HTMLParseError(ValueError):
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ def write_piff_header(stream, params):
|
|||
sample_entry_payload += u16.pack(0x18) # depth
|
||||
sample_entry_payload += s16.pack(-1) # pre defined
|
||||
|
||||
codec_private_data = binascii.unhexlify(params['codec_private_data'].encode())
|
||||
codec_private_data = binascii.unhexlify(params['codec_private_data'])
|
||||
if fourcc in ('H264', 'AVC1'):
|
||||
sps, pps = codec_private_data.split(u32.pack(1))[1:]
|
||||
avcc_payload = u8.pack(1) # configuration version
|
||||
|
|
|
|||
|
|
@ -432,29 +432,28 @@ class InfoExtractor:
|
|||
|
||||
chapter: Name or title of the chapter the video belongs to.
|
||||
chapter_number: Number of the chapter the video belongs to, as an integer.
|
||||
chapter_id: Id of the chapter the video belongs to, as a unicode string.
|
||||
chapter_id: Id of the chapter the video belongs to.
|
||||
|
||||
The following fields should only be used when the video is an episode of some
|
||||
series, programme or podcast:
|
||||
|
||||
series: Title of the series or programme the video episode belongs to.
|
||||
series_id: Id of the series or programme the video episode belongs to, as a unicode string.
|
||||
series_id: Id of the series or programme the video episode belongs to.
|
||||
season: Title of the season the video episode belongs to.
|
||||
season_number: Number of the season the video episode belongs to, as an integer.
|
||||
season_id: Id of the season the video episode belongs to, as a unicode string.
|
||||
season_id: Id of the season the video episode belongs to.
|
||||
episode: Title of the video episode. Unlike mandatory video title field,
|
||||
this field should denote the exact title of the video episode
|
||||
without any kind of decoration.
|
||||
episode_number: Number of the video episode within a season, as an integer.
|
||||
episode_id: Id of the video episode, as a unicode string.
|
||||
episode_id: Id of the video episode.
|
||||
|
||||
The following fields should only be used when the media is a track or a part of
|
||||
a music album:
|
||||
|
||||
track: Title of the track.
|
||||
track_number: Number of the track within an album or a disc, as an integer.
|
||||
track_id: Id of the track (useful in case of custom indexing, e.g. 6.iii),
|
||||
as a unicode string.
|
||||
track_id: Id of the track (useful for custom indexing, e.g. 6.iii).
|
||||
artists: List of artists of the track.
|
||||
composers: List of composers of the piece.
|
||||
genres: List of genres of the track.
|
||||
|
|
@ -487,7 +486,7 @@ class InfoExtractor:
|
|||
creator: Use "creators" instead.
|
||||
The creator of the video.
|
||||
|
||||
Unless mentioned otherwise, the fields should be Unicode strings.
|
||||
Unless mentioned otherwise, the fields should be strings.
|
||||
|
||||
Unless mentioned otherwise, None is equivalent to absence of information.
|
||||
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ def _get_subtitles(self, video_id, variants, ios_playlist_url, headers, *args, *
|
|||
# See: https://github.com/yt-dlp/yt-dlp/issues/986
|
||||
platform_tag_subs, featureset_subs = next(
|
||||
((platform_tag, featureset)
|
||||
for platform_tag, featuresets in reversed(list(variants.items())) for featureset in featuresets
|
||||
for platform_tag, featuresets in reversed(variants.items()) for featureset in featuresets
|
||||
if try_get(featureset, lambda x: x[2]) == 'outband-webvtt'),
|
||||
(None, None))
|
||||
|
||||
|
|
@ -143,7 +143,7 @@ def _real_extract(self, url):
|
|||
# See: https://github.com/yt-dlp/yt-dlp/issues/986
|
||||
platform_tag_video, featureset_video = next(
|
||||
((platform_tag, featureset)
|
||||
for platform_tag, featuresets in reversed(list(variants.items())) for featureset in featuresets
|
||||
for platform_tag, featuresets in reversed(variants.items()) for featureset in featuresets
|
||||
if set(try_get(featureset, lambda x: x[:2]) or []) == {'aes', 'hls'}),
|
||||
(None, None))
|
||||
if not platform_tag_video or not featureset_video:
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ def error(self, message: str, cause=None):
|
|||
if self.log_level <= self.LogLevel.ERROR:
|
||||
self.__ie._downloader.report_error(
|
||||
self._format_msg(message), is_error=False,
|
||||
tb=''.join(traceback.format_exception(None, cause, cause.__traceback__)) if cause else None)
|
||||
tb=''.join(traceback.format_exception(cause)) if cause else None)
|
||||
|
||||
|
||||
class PoTokenCache:
|
||||
|
|
|
|||
|
|
@ -108,9 +108,7 @@ def make_ssl_context(
|
|||
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
||||
context.check_hostname = verify
|
||||
context.verify_mode = ssl.CERT_REQUIRED if verify else ssl.CERT_NONE
|
||||
# OpenSSL 1.1.1+ Python 3.8+ keylog file
|
||||
if hasattr(context, 'keylog_filename'):
|
||||
context.keylog_filename = os.environ.get('SSLKEYLOGFILE') or None
|
||||
context.keylog_filename = os.environ.get('SSLKEYLOGFILE') or None
|
||||
|
||||
# Some servers may reject requests if ALPN extension is not sent. See:
|
||||
# https://github.com/python/cpython/issues/85140
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ def _real_read(self, amt: int | None = None) -> bytes:
|
|||
# Work around issue with `.read(amt)` then `.read()`
|
||||
# See: https://github.com/urllib3/urllib3/issues/3636
|
||||
if amt is None:
|
||||
# compat: py3.9: Python 3.9 preallocates the whole read buffer, read in chunks
|
||||
# Read in chunks to avoid preallocating a large buffer
|
||||
read_chunk = functools.partial(self.fp.read, 1 << 20, decode_content=True)
|
||||
return b''.join(iter(read_chunk, b''))
|
||||
# Interact with urllib3 response directly.
|
||||
|
|
|
|||
|
|
@ -296,13 +296,9 @@ class UrllibResponseAdapter(Response):
|
|||
"""
|
||||
|
||||
def __init__(self, res: http.client.HTTPResponse | urllib.response.addinfourl):
|
||||
# addinfourl: In Python 3.9+, .status was introduced and .getcode() was deprecated [1]
|
||||
# HTTPResponse: .getcode() was deprecated, .status always existed [2]
|
||||
# 1. https://docs.python.org/3/library/urllib.request.html#urllib.response.addinfourl.getcode
|
||||
# 2. https://docs.python.org/3.10/library/http.client.html#http.client.HTTPResponse.status
|
||||
super().__init__(
|
||||
fp=res, headers=res.headers, url=res.url,
|
||||
status=getattr(res, 'status', None) or res.getcode(), reason=getattr(res, 'reason', None))
|
||||
status=res.status, reason=getattr(res, 'reason', None))
|
||||
|
||||
def read(self, amt=None):
|
||||
if self.closed:
|
||||
|
|
|
|||
|
|
@ -238,11 +238,9 @@ def find_xpath_attr(node, xpath, key, val=None):
|
|||
expr = xpath + (f'[@{key}]' if val is None else f"[@{key}='{val}']")
|
||||
return node.find(expr)
|
||||
|
||||
# On python2.6 the xml.etree.ElementTree.Element methods don't support
|
||||
# the namespace parameter
|
||||
|
||||
|
||||
def xpath_with_ns(path, ns_map):
|
||||
"""Expand namespace-prefixed names to Clark notation."""
|
||||
components = [c.split(':') for c in path.split('/')]
|
||||
replaced = []
|
||||
for c in components:
|
||||
|
|
@ -876,7 +874,7 @@ def __init__(self, args, *remaining, env=None, text=False, shell=False, **kwargs
|
|||
|
||||
self.__text_mode = kwargs.get('encoding') or kwargs.get('errors') or text or kwargs.get('universal_newlines')
|
||||
if text is True:
|
||||
kwargs['universal_newlines'] = True # For 3.6 compatibility
|
||||
kwargs['text'] = True
|
||||
kwargs.setdefault('encoding', 'utf-8')
|
||||
kwargs.setdefault('errors', 'replace')
|
||||
|
||||
|
|
@ -1012,7 +1010,7 @@ def __msg(self):
|
|||
def format_traceback(self):
|
||||
return join_nonempty(
|
||||
self.traceback and ''.join(traceback.format_tb(self.traceback)),
|
||||
self.cause and ''.join(traceback.format_exception(None, self.cause, self.cause.__traceback__)[1:]),
|
||||
self.cause and ''.join(traceback.format_exception(self.cause)[1:]),
|
||||
delim='\n') or None
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
|
|
@ -1960,11 +1958,7 @@ def setproctitle(title):
|
|||
libc = ctypes.cdll.LoadLibrary('libc.so.6')
|
||||
except OSError:
|
||||
return
|
||||
except TypeError:
|
||||
# LoadLibrary in Windows Python 2.7.13 only expects
|
||||
# a bytestring, but since unicode_literals turns
|
||||
# every string into a unicode string, it fails.
|
||||
return
|
||||
|
||||
title_bytes = title.encode()
|
||||
buf = ctypes.create_string_buffer(len(title_bytes))
|
||||
buf.value = title_bytes
|
||||
|
|
@ -2655,11 +2649,10 @@ def multipart_encode(data, boundary=None):
|
|||
Encode a dict to RFC 7578-compliant form-data
|
||||
|
||||
data:
|
||||
A dict where keys and values can be either Unicode or bytes-like
|
||||
objects.
|
||||
A dict where keys and values can be either str or bytes-like objects.
|
||||
boundary:
|
||||
If specified a Unicode object, it's used as the boundary. Otherwise
|
||||
a random boundary is generated.
|
||||
An ASCII string to use as the boundary. If omitted, a random boundary
|
||||
is generated.
|
||||
|
||||
Reference: https://tools.ietf.org/html/rfc7578
|
||||
"""
|
||||
|
|
@ -3422,7 +3415,7 @@ def ass_subtitles_timecode(seconds):
|
|||
def dfxp2srt(dfxp_data):
|
||||
"""
|
||||
@param dfxp_data A bytes-like object containing DFXP data
|
||||
@returns A unicode object containing converted SRT data
|
||||
@returns A string containing the converted SRT data
|
||||
"""
|
||||
LEGACY_NAMESPACES = (
|
||||
(b'http://www.w3.org/ns/ttml', [
|
||||
|
|
|
|||
Loading…
Reference in a new issue