diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/conftest.py | 10 | ||||
-rw-r--r-- | test/helper.py | 32 | ||||
-rw-r--r-- | test/test_InfoExtractor.py | 138 | ||||
-rw-r--r-- | test/test_YoutubeDL.py | 45 | ||||
-rw-r--r-- | test/test_aes.py | 12 | ||||
-rw-r--r-- | test/test_compat.py | 10 | ||||
-rw-r--r-- | test/test_config.py | 2 | ||||
-rw-r--r-- | test/test_cookies.py | 133 | ||||
-rwxr-xr-x | test/test_download.py | 20 | ||||
-rw-r--r-- | test/test_downloader_http.py | 6 | ||||
-rw-r--r-- | test/test_http_proxy.py | 4 | ||||
-rw-r--r-- | test/test_iqiyi_sdk_interpreter.py | 4 | ||||
-rw-r--r-- | test/test_jsinterp.py | 1 | ||||
-rw-r--r-- | test/test_netrc.py | 2 | ||||
-rw-r--r-- | test/test_networking.py | 56 | ||||
-rw-r--r-- | test/test_networking_utils.py | 12 | ||||
-rw-r--r-- | test/test_overwrites.py | 4 | ||||
-rw-r--r-- | test/test_plugins.py | 2 | ||||
-rw-r--r-- | test/test_post_hooks.py | 2 | ||||
-rw-r--r-- | test/test_postprocessors.py | 137 | ||||
-rw-r--r-- | test/test_socks.py | 8 | ||||
-rw-r--r-- | test/test_subtitles.py | 11 | ||||
-rw-r--r-- | test/test_traversal.py | 6 | ||||
-rw-r--r-- | test/test_update.py | 8 | ||||
-rw-r--r-- | test/test_utils.py | 95 | ||||
-rw-r--r-- | test/test_websockets.py | 4 | ||||
-rw-r--r-- | test/test_youtube_misc.py | 2 | ||||
-rw-r--r-- | test/test_youtube_signature.py | 12 |
28 files changed, 419 insertions, 359 deletions
diff --git a/test/conftest.py b/test/conftest.py index decd2c8..a8b92f8 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -22,8 +22,8 @@ def handler(request): class HandlerWrapper(handler): RH_KEY = handler.RH_KEY - def __init__(self, *args, **kwargs): - super().__init__(logger=FakeLogger, *args, **kwargs) + def __init__(self, **kwargs): + super().__init__(logger=FakeLogger, **kwargs) return HandlerWrapper @@ -54,11 +54,11 @@ def skip_handlers_if(request, handler): def pytest_configure(config): config.addinivalue_line( - "markers", "skip_handler(handler): skip test for the given handler", + 'markers', 'skip_handler(handler): skip test for the given handler', ) config.addinivalue_line( - "markers", "skip_handler_if(handler): skip test for the given handler if condition is true" + 'markers', 'skip_handler_if(handler): skip test for the given handler if condition is true', ) config.addinivalue_line( - "markers", "skip_handlers_if(handler): skip test for handlers when the condition is true" + 'markers', 'skip_handlers_if(handler): skip test for handlers when the condition is true', ) diff --git a/test/helper.py b/test/helper.py index e747312..3b550d1 100644 --- a/test/helper.py +++ b/test/helper.py @@ -16,8 +16,8 @@ if 'pytest' in sys.modules: import pytest is_download_test = pytest.mark.download else: - def is_download_test(testClass): - return testClass + def is_download_test(test_class): + return test_class def get_params(override=None): @@ -45,10 +45,10 @@ def try_rm(filename): def report_warning(message, *args, **kwargs): - ''' + """ Print the message to stderr, it will be prefixed with 'WARNING:' If stderr is a tty file the 'WARNING:' will be colored - ''' + """ if sys.stderr.isatty() and compat_os_name != 'nt': _msg_header = '\033[0;33mWARNING:\033[0m' else: @@ -138,15 +138,14 @@ def expect_value(self, got, expected, field): elif isinstance(expected, list) and isinstance(got, list): self.assertEqual( len(expected), len(got), - 'Expect a list of length %d, but got a list of length %d for field %s' % ( - len(expected), len(got), field)) + f'Expect a list of length {len(expected)}, but got a list of length {len(got)} for field {field}') for index, (item_got, item_expected) in enumerate(zip(got, expected)): type_got = type(item_got) type_expected = type(item_expected) self.assertEqual( type_expected, type_got, - 'Type mismatch for list item at index %d for field %s, expected %r, got %r' % ( - index, field, type_expected, type_got)) + f'Type mismatch for list item at index {index} for field {field}, ' + f'expected {type_expected!r}, got {type_got!r}') expect_value(self, item_got, item_expected, field) else: if isinstance(expected, str) and expected.startswith('md5:'): @@ -224,7 +223,7 @@ def sanitize_got_info_dict(got_dict): test_info_dict.pop('display_id') # Remove deprecated fields - for old in YoutubeDL._deprecated_multivalue_fields.keys(): + for old in YoutubeDL._deprecated_multivalue_fields: test_info_dict.pop(old, None) # release_year may be generated from release_date @@ -246,11 +245,11 @@ def expect_info_dict(self, got_dict, expected_dict): if expected_dict.get('ext'): mandatory_fields.extend(('url', 'ext')) for key in mandatory_fields: - self.assertTrue(got_dict.get(key), 'Missing mandatory field %s' % key) + self.assertTrue(got_dict.get(key), f'Missing mandatory field {key}') # Check for mandatory fields that are automatically set by YoutubeDL if got_dict.get('_type', 'video') == 'video': for key in ['webpage_url', 'extractor', 'extractor_key']: - self.assertTrue(got_dict.get(key), 'Missing field: %s' % key) + self.assertTrue(got_dict.get(key), f'Missing field: {key}') test_info_dict = sanitize_got_info_dict(got_dict) @@ -258,7 +257,7 @@ def expect_info_dict(self, got_dict, expected_dict): if missing_keys: def _repr(v): if isinstance(v, str): - return "'%s'" % v.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n') + return "'{}'".format(v.replace('\\', '\\\\').replace("'", "\\'").replace('\n', '\\n')) elif isinstance(v, type): return v.__name__ else: @@ -275,8 +274,7 @@ def expect_info_dict(self, got_dict, expected_dict): write_string(info_dict_str.replace('\n', '\n '), out=sys.stderr) self.assertFalse( missing_keys, - 'Missing keys in test definition: %s' % ( - ', '.join(sorted(missing_keys)))) + 'Missing keys in test definition: {}'.format(', '.join(sorted(missing_keys)))) def assertRegexpMatches(self, text, regexp, msg=None): @@ -285,9 +283,9 @@ def assertRegexpMatches(self, text, regexp, msg=None): else: m = re.match(regexp, text) if not m: - note = 'Regexp didn\'t match: %r not found' % (regexp) + note = f'Regexp didn\'t match: {regexp!r} not found' if len(text) < 1000: - note += ' in %r' % text + note += f' in {text!r}' if msg is None: msg = note else: @@ -310,7 +308,7 @@ def assertLessEqual(self, got, expected, msg=None): def assertEqual(self, got, expected, msg=None): - if not (got == expected): + if got != expected: if msg is None: msg = f'{got!r} not equal to {expected!r}' self.assertTrue(got == expected, msg) diff --git a/test/test_InfoExtractor.py b/test/test_InfoExtractor.py index 744587e..31e8f82 100644 --- a/test/test_InfoExtractor.py +++ b/test/test_InfoExtractor.py @@ -262,19 +262,19 @@ class TestInfoExtractor(unittest.TestCase): ''', { 'chapters': [ - {"title": "Explosie Turnhout", "start_time": 70, "end_time": 440}, - {"title": "Jaarwisseling", "start_time": 440, "end_time": 1179}, - {"title": "Natuurbranden Colorado", "start_time": 1179, "end_time": 1263}, - {"title": "Klimaatverandering", "start_time": 1263, "end_time": 1367}, - {"title": "Zacht weer", "start_time": 1367, "end_time": 1383}, - {"title": "Financiële balans", "start_time": 1383, "end_time": 1484}, - {"title": "Club Brugge", "start_time": 1484, "end_time": 1575}, - {"title": "Mentale gezondheid bij topsporters", "start_time": 1575, "end_time": 1728}, - {"title": "Olympische Winterspelen", "start_time": 1728, "end_time": 1873}, - {"title": "Sober oudjaar in Nederland", "start_time": 1873, "end_time": 2079.23} + {'title': 'Explosie Turnhout', 'start_time': 70, 'end_time': 440}, + {'title': 'Jaarwisseling', 'start_time': 440, 'end_time': 1179}, + {'title': 'Natuurbranden Colorado', 'start_time': 1179, 'end_time': 1263}, + {'title': 'Klimaatverandering', 'start_time': 1263, 'end_time': 1367}, + {'title': 'Zacht weer', 'start_time': 1367, 'end_time': 1383}, + {'title': 'Financiële balans', 'start_time': 1383, 'end_time': 1484}, + {'title': 'Club Brugge', 'start_time': 1484, 'end_time': 1575}, + {'title': 'Mentale gezondheid bij topsporters', 'start_time': 1575, 'end_time': 1728}, + {'title': 'Olympische Winterspelen', 'start_time': 1728, 'end_time': 1873}, + {'title': 'Sober oudjaar in Nederland', 'start_time': 1873, 'end_time': 2079.23}, ], - 'title': 'Het journaal - Aflevering 365 (Seizoen 2021)' - }, {} + 'title': 'Het journaal - Aflevering 365 (Seizoen 2021)', + }, {}, ), ( # test multiple thumbnails in a list @@ -301,13 +301,13 @@ class TestInfoExtractor(unittest.TestCase): 'thumbnails': [{'url': 'https://www.rainews.it/cropgd/640x360/dl/img/2021/12/30/1640886376927_GettyImages.jpg'}], }, {}, - ) + ), ] for html, expected_dict, search_json_ld_kwargs in _TESTS: expect_dict( self, self.ie._search_json_ld(html, None, **search_json_ld_kwargs), - expected_dict + expected_dict, ) def test_download_json(self): @@ -366,7 +366,7 @@ class TestInfoExtractor(unittest.TestCase): 'height': 740, 'tbr': 1500, }], - 'thumbnail': '//pics.r18.com/digital/amateur/mgmr105/mgmr105jp.jpg' + 'thumbnail': '//pics.r18.com/digital/amateur/mgmr105/mgmr105jp.jpg', }) # from https://www.csfd.cz/ @@ -419,9 +419,9 @@ class TestInfoExtractor(unittest.TestCase): 'height': 1080, }], 'subtitles': { - 'cs': [{'url': 'https://video.csfd.cz/files/subtitles/163/344/163344115_4c388b.srt'}] + 'cs': [{'url': 'https://video.csfd.cz/files/subtitles/163/344/163344115_4c388b.srt'}], }, - 'thumbnail': 'https://img.csfd.cz/files/images/film/video/preview/163/344/163344118_748d20.png?h360' + 'thumbnail': 'https://img.csfd.cz/files/images/film/video/preview/163/344/163344118_748d20.png?h360', }) # from https://tamasha.com/v/Kkdjw @@ -452,7 +452,7 @@ class TestInfoExtractor(unittest.TestCase): 'ext': 'mp4', 'format_id': '144p', 'height': 144, - }] + }], }) # from https://www.directvnow.com @@ -470,7 +470,7 @@ class TestInfoExtractor(unittest.TestCase): 'formats': [{ 'ext': 'mp4', 'url': 'https://cdn.directv.com/content/dam/dtv/prod/website_directvnow-international/videos/DTVN_hdr_HBO_v3.mp4', - }] + }], }) # from https://www.directvnow.com @@ -488,7 +488,7 @@ class TestInfoExtractor(unittest.TestCase): 'formats': [{ 'url': 'https://cdn.directv.com/content/dam/dtv/prod/website_directvnow-international/videos/DTVN_hdr_HBO_v3.mp4', 'ext': 'mp4', - }] + }], }) # from https://www.klarna.com/uk/ @@ -547,8 +547,8 @@ class TestInfoExtractor(unittest.TestCase): 'id': 'XEgvuql4', 'formats': [{ 'url': 'rtmp://192.138.214.154/live/sjclive', - 'ext': 'flv' - }] + 'ext': 'flv', + }], }) # from https://www.pornoxo.com/videos/7564/striptease-from-sexy-secretary/ @@ -588,8 +588,8 @@ class TestInfoExtractor(unittest.TestCase): 'thumbnail': 'https://t03.vipstreamservice.com/thumbs/pxo-full/2009-12/14/a4b2157147afe5efa93ce1978e0265289c193874e02597.flv-full-13.jpg', 'formats': [{ 'url': 'https://cdn.pornoxo.com/key=MF+oEbaxqTKb50P-w9G3nA,end=1489689259,ip=104.199.146.27/ip=104.199.146.27/speed=6573765/buffer=3.0/2009-12/4b2157147afe5efa93ce1978e0265289c193874e02597.flv', - 'ext': 'flv' - }] + 'ext': 'flv', + }], }) # from http://www.indiedb.com/games/king-machine/videos @@ -610,12 +610,12 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'formats': [{ 'url': 'http://cdn.dbolical.com/cache/videos/games/1/50/49678/encode_mp4/king-machine-trailer.mp4', 'height': 360, - 'ext': 'mp4' + 'ext': 'mp4', }, { 'url': 'http://cdn.dbolical.com/cache/videos/games/1/50/49678/encode720p_mp4/king-machine-trailer.mp4', 'height': 720, - 'ext': 'mp4' - }] + 'ext': 'mp4', + }], }) def test_parse_m3u8_formats(self): @@ -866,7 +866,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'height': 1080, 'vcodec': 'avc1.64002a', }], - {} + {}, ), ( 'bipbop_16x9', @@ -990,45 +990,45 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'en': [{ 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/eng/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }, { 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/eng_forced/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }], 'fr': [{ 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/fra/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }, { 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/fra_forced/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }], 'es': [{ 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/spa/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }, { 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/spa_forced/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }], 'ja': [{ 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/jpn/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }, { 'url': 'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/subtitles/jpn_forced/prog_index.m3u8', 'ext': 'vtt', - 'protocol': 'm3u8_native' + 'protocol': 'm3u8_native', }], - } + }, ), ] for m3u8_file, m3u8_url, expected_formats, expected_subs in _TEST_CASES: - with open('./test/testdata/m3u8/%s.m3u8' % m3u8_file, encoding='utf-8') as f: + with open(f'./test/testdata/m3u8/{m3u8_file}.m3u8', encoding='utf-8') as f: formats, subs = self.ie._parse_m3u8_formats_and_subtitles( f.read(), m3u8_url, ext='mp4') self.ie._sort_formats(formats) @@ -1366,14 +1366,14 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'url': 'https://sdn-global-streaming-cache-3qsdn.akamaized.net/stream/3144/files/17/07/672975/3144-kZT4LWMQw6Rh7Kpd.ism/manifest.mpd', 'fragment_base_url': 'https://sdn-global-streaming-cache-3qsdn.akamaized.net/stream/3144/files/17/07/672975/3144-kZT4LWMQw6Rh7Kpd.ism/dash/', 'protocol': 'http_dash_segments', - } - ] + }, + ], }, - ) + ), ] for mpd_file, mpd_url, mpd_base_url, expected_formats, expected_subtitles in _TEST_CASES: - with open('./test/testdata/mpd/%s.mpd' % mpd_file, encoding='utf-8') as f: + with open(f'./test/testdata/mpd/{mpd_file}.mpd', encoding='utf-8') as f: formats, subtitles = self.ie._parse_mpd_formats_and_subtitles( compat_etree_fromstring(f.read().encode()), mpd_base_url=mpd_base_url, mpd_url=mpd_url) @@ -1408,7 +1408,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'sampling_rate': 48000, 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video-100', @@ -1431,7 +1431,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401FDA0544EFFC2D002CBC40000003004000000C03C60CA80000000168EF32C8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video-326', @@ -1454,7 +1454,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401FDA0241FE23FFC3BC83BA44000003000400000300C03C60CA800000000168EF32C8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video-698', @@ -1477,7 +1477,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401FDA0350BFB97FF06AF06AD1000003000100000300300F1832A00000000168EF32C8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video-1493', @@ -1500,7 +1500,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401FDA011C3DE6FFF0D890D871000003000100000300300F1832A00000000168EF32C8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video-4482', @@ -1523,7 +1523,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401FDA01A816F97FFC1ABC1AB440000003004000000C03C60CA80000000168EF32C8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }], { @@ -1538,10 +1538,10 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'duration': 8880746666, 'timescale': 10000000, 'fourcc': 'TTML', - 'codec_private_data': '' - } - } - ] + 'codec_private_data': '', + }, + }, + ], }, ), ( @@ -1571,7 +1571,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'sampling_rate': 48000, 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'audio_deu_1-224', @@ -1597,7 +1597,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'sampling_rate': 48000, 'channels': 6, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-23', @@ -1622,7 +1622,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '000000016742C00CDB06077E5C05A808080A00000300020000030009C0C02EE0177CC6300F142AE00000000168CA8DC8', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-403', @@ -1647,7 +1647,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D4014E98323B602D4040405000003000100000300320F1429380000000168EAECF2', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-680', @@ -1672,7 +1672,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401EE981405FF2E02D4040405000000300100000030320F162D3800000000168EAECF2', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-1253', @@ -1698,7 +1698,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401EE981405FF2E02D4040405000000300100000030320F162D3800000000168EAECF2', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-2121', @@ -1723,7 +1723,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D401EECA0601BD80B50101014000003000400000300C83C58B6580000000168E93B3C80', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-3275', @@ -1748,7 +1748,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D4020ECA02802DD80B501010140000003004000000C83C60C65800000000168E93B3C80', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-5300', @@ -1773,7 +1773,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D4028ECA03C0113F2E02D4040405000000300100000030320F18319600000000168E93B3C80', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }, { 'format_id': 'video_deu-8079', @@ -1798,7 +1798,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'codec_private_data': '00000001674D4028ECA03C0113F2E02D4040405000000300100000030320F18319600000000168E93B3C80', 'channels': 2, 'bits_per_sample': 16, - 'nal_unit_length_field': 4 + 'nal_unit_length_field': 4, }, }], {}, @@ -1806,7 +1806,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ ] for ism_file, ism_url, expected_formats, expected_subtitles in _TEST_CASES: - with open('./test/testdata/ism/%s.Manifest' % ism_file, encoding='utf-8') as f: + with open(f'./test/testdata/ism/{ism_file}.Manifest', encoding='utf-8') as f: formats, subtitles = self.ie._parse_ism_formats_and_subtitles( compat_etree_fromstring(f.read().encode()), ism_url=ism_url) self.ie._sort_formats(formats) @@ -1827,12 +1827,12 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ 'tbr': 2148, 'width': 1280, 'height': 720, - }] + }], ), ] for f4m_file, f4m_url, expected_formats in _TEST_CASES: - with open('./test/testdata/f4m/%s.f4m' % f4m_file, encoding='utf-8') as f: + with open(f'./test/testdata/f4m/{f4m_file}.f4m', encoding='utf-8') as f: formats = self.ie._parse_f4m_formats( compat_etree_fromstring(f.read().encode()), f4m_url, None) @@ -1873,13 +1873,13 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ }, { 'manifest_url': 'https://example.org/src/foo_xspf.xspf', 'url': 'https://example.com/track3.mp3', - }] - }] + }], + }], ), ] for xspf_file, xspf_url, expected_entries in _TEST_CASES: - with open('./test/testdata/xspf/%s.xspf' % xspf_file, encoding='utf-8') as f: + with open(f'./test/testdata/xspf/{xspf_file}.xspf', encoding='utf-8') as f: entries = self.ie._parse_xspf( compat_etree_fromstring(f.read().encode()), xspf_file, xspf_url=xspf_url, xspf_base_url=xspf_url) @@ -1902,7 +1902,7 @@ jwplayer("mediaplayer").setup({"abouttext":"Visit Indie DB","aboutlink":"http:\/ server_thread.start() (content, urlh) = self.ie._download_webpage_handle( - 'http://127.0.0.1:%d/teapot' % port, None, + f'http://127.0.0.1:{port}/teapot', None, expected_status=TEAPOT_RESPONSE_STATUS) self.assertEqual(content, TEAPOT_RESPONSE_BODY) diff --git a/test/test_YoutubeDL.py b/test/test_YoutubeDL.py index 5242cf8..841ce1a 100644 --- a/test/test_YoutubeDL.py +++ b/test/test_YoutubeDL.py @@ -8,6 +8,7 @@ import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import contextlib import copy import json @@ -129,8 +130,8 @@ class TestFormatSelection(unittest.TestCase): 'allow_multiple_audio_streams': multi, }) ydl.process_ie_result(info_dict.copy()) - downloaded = map(lambda x: x['format_id'], ydl.downloaded_info_dicts) - self.assertEqual(list(downloaded), list(expected)) + downloaded = [x['format_id'] for x in ydl.downloaded_info_dicts] + self.assertEqual(downloaded, list(expected)) test('20/47', '47') test('20/71/worst', '35') @@ -515,10 +516,8 @@ class TestFormatSelection(unittest.TestCase): self.assertEqual(downloaded_ids, ['D', 'C', 'B']) ydl = YDL({'format': 'best[height<40]'}) - try: + with contextlib.suppress(ExtractorError): ydl.process_ie_result(info_dict) - except ExtractorError: - pass self.assertEqual(ydl.downloaded_info_dicts, []) def test_default_format_spec(self): @@ -652,8 +651,8 @@ class TestYoutubeDL(unittest.TestCase): 'formats': [ {'id': 'id 1', 'height': 1080, 'width': 1920}, {'id': 'id 2', 'height': 720}, - {'id': 'id 3'} - ] + {'id': 'id 3'}, + ], } def test_prepare_outtmpl_and_filename(self): @@ -773,7 +772,7 @@ class TestYoutubeDL(unittest.TestCase): test('%(formats)j', (json.dumps(FORMATS), None)) test('%(formats)#j', ( json.dumps(FORMATS, indent=4), - json.dumps(FORMATS, indent=4).replace(':', ':').replace('"', """).replace('\n', ' ') + json.dumps(FORMATS, indent=4).replace(':', ':').replace('"', '"').replace('\n', ' '), )) test('%(title5).3B', 'á') test('%(title5)U', 'áéí 𝐀') @@ -843,8 +842,8 @@ class TestYoutubeDL(unittest.TestCase): # Empty filename test('%(foo|)s-%(bar|)s.%(ext)s', '-.mp4') - # test('%(foo|)s.%(ext)s', ('.mp4', '_.mp4')) # fixme - # test('%(foo|)s', ('', '_')) # fixme + # test('%(foo|)s.%(ext)s', ('.mp4', '_.mp4')) # FIXME: ? + # test('%(foo|)s', ('', '_')) # FIXME: ? # Environment variable expansion for prepare_filename os.environ['__yt_dlp_var'] = 'expanded' @@ -861,7 +860,7 @@ class TestYoutubeDL(unittest.TestCase): test('Hello %(title1)s', 'Hello $PATH') test('Hello %(title2)s', 'Hello %PATH%') test('%(title3)s', ('foo/bar\\test', 'foo⧸bar⧹test')) - test('folder/%(title3)s', ('folder/foo/bar\\test', 'folder%sfoo⧸bar⧹test' % os.path.sep)) + test('folder/%(title3)s', ('folder/foo/bar\\test', f'folder{os.path.sep}foo⧸bar⧹test')) def test_format_note(self): ydl = YoutubeDL() @@ -883,22 +882,22 @@ class TestYoutubeDL(unittest.TestCase): f.write('EXAMPLE') return [info['filepath']], info - def run_pp(params, PP): + def run_pp(params, pp): with open(filename, 'w') as f: f.write('EXAMPLE') ydl = YoutubeDL(params) - ydl.add_post_processor(PP()) + ydl.add_post_processor(pp()) ydl.post_process(filename, {'filepath': filename}) run_pp({'keepvideo': True}, SimplePP) - self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename) - self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile) + self.assertTrue(os.path.exists(filename), f'{filename} doesn\'t exist') + self.assertTrue(os.path.exists(audiofile), f'{audiofile} doesn\'t exist') os.unlink(filename) os.unlink(audiofile) run_pp({'keepvideo': False}, SimplePP) - self.assertFalse(os.path.exists(filename), '%s exists' % filename) - self.assertTrue(os.path.exists(audiofile), '%s doesn\'t exist' % audiofile) + self.assertFalse(os.path.exists(filename), f'{filename} exists') + self.assertTrue(os.path.exists(audiofile), f'{audiofile} doesn\'t exist') os.unlink(audiofile) class ModifierPP(PostProcessor): @@ -908,7 +907,7 @@ class TestYoutubeDL(unittest.TestCase): return [], info run_pp({'keepvideo': False}, ModifierPP) - self.assertTrue(os.path.exists(filename), '%s doesn\'t exist' % filename) + self.assertTrue(os.path.exists(filename), f'{filename} doesn\'t exist') os.unlink(filename) def test_match_filter(self): @@ -920,7 +919,7 @@ class TestYoutubeDL(unittest.TestCase): 'duration': 30, 'filesize': 10 * 1024, 'playlist_id': '42', - 'uploader': "變態妍字幕版 太妍 тест", + 'uploader': '變態妍字幕版 太妍 тест', 'creator': "тест ' 123 ' тест--", 'webpage_url': 'http://example.com/watch?v=shenanigans', } @@ -933,7 +932,7 @@ class TestYoutubeDL(unittest.TestCase): 'description': 'foo', 'filesize': 5 * 1024, 'playlist_id': '43', - 'uploader': "тест 123", + 'uploader': 'тест 123', 'webpage_url': 'http://example.com/watch?v=SHENANIGANS', } videos = [first, second] @@ -1180,7 +1179,7 @@ class TestYoutubeDL(unittest.TestCase): }) return { 'id': video_id, - 'title': 'Video %s' % video_id, + 'title': f'Video {video_id}', 'formats': formats, } @@ -1194,8 +1193,8 @@ class TestYoutubeDL(unittest.TestCase): '_type': 'url_transparent', 'ie_key': VideoIE.ie_key(), 'id': video_id, - 'url': 'video:%s' % video_id, - 'title': 'Video Transparent %s' % video_id, + 'url': f'video:{video_id}', + 'title': f'Video Transparent {video_id}', } def _real_extract(self, url): diff --git a/test/test_aes.py b/test/test_aes.py index a26abfd..5f975ef 100644 --- a/test/test_aes.py +++ b/test/test_aes.py @@ -87,7 +87,7 @@ class TestAES(unittest.TestCase): password = intlist_to_bytes(self.key).decode() encrypted = base64.b64encode( intlist_to_bytes(self.iv[:8]) - + b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae' + + b'\x17\x15\x93\xab\x8d\x80V\xcdV\xe0\t\xcdo\xc2\xa5\xd8ksM\r\xe27N\xae', ).decode() decrypted = (aes_decrypt_text(encrypted, password, 16)) self.assertEqual(decrypted, self.secret_msg) @@ -95,7 +95,7 @@ class TestAES(unittest.TestCase): password = intlist_to_bytes(self.key).decode() encrypted = base64.b64encode( intlist_to_bytes(self.iv[:8]) - + b'\x0b\xe6\xa4\xd9z\x0e\xb8\xb9\xd0\xd4i_\x85\x1d\x99\x98_\xe5\x80\xe7.\xbf\xa5\x83' + + b'\x0b\xe6\xa4\xd9z\x0e\xb8\xb9\xd0\xd4i_\x85\x1d\x99\x98_\xe5\x80\xe7.\xbf\xa5\x83', ).decode() decrypted = (aes_decrypt_text(encrypted, password, 32)) self.assertEqual(decrypted, self.secret_msg) @@ -132,16 +132,16 @@ class TestAES(unittest.TestCase): block = [0x21, 0xA0, 0x43, 0xFF] self.assertEqual(pad_block(block, 'pkcs7'), - block + [0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C]) + [*block, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C]) self.assertEqual(pad_block(block, 'iso7816'), - block + [0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + [*block, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) self.assertEqual(pad_block(block, 'whitespace'), - block + [0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20]) + [*block, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20]) self.assertEqual(pad_block(block, 'zero'), - block + [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) + [*block, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) block = list(range(16)) for mode in ('pkcs7', 'iso7816', 'whitespace', 'zero'): diff --git a/test/test_compat.py b/test/test_compat.py index 71ca7f9..e7d97e3 100644 --- a/test/test_compat.py +++ b/test/test_compat.py @@ -15,8 +15,8 @@ from yt_dlp.compat import urllib # isort: split from yt_dlp.compat import ( compat_etree_fromstring, compat_expanduser, - compat_urllib_parse_unquote, - compat_urllib_parse_urlencode, + compat_urllib_parse_unquote, # noqa: TID251 + compat_urllib_parse_urlencode, # noqa: TID251 ) from yt_dlp.compat.urllib.request import getproxies @@ -24,15 +24,15 @@ from yt_dlp.compat.urllib.request import getproxies class TestCompat(unittest.TestCase): def test_compat_passthrough(self): with self.assertWarns(DeprecationWarning): - compat.compat_basestring + _ = compat.compat_basestring with self.assertWarns(DeprecationWarning): - compat.WINDOWS_VT_MODE + _ = compat.WINDOWS_VT_MODE self.assertEqual(urllib.request.getproxies, getproxies) with self.assertWarns(DeprecationWarning): - compat.compat_pycrypto_AES # Must not raise error + _ = compat.compat_pycrypto_AES # Must not raise error def test_compat_expanduser(self): old_home = os.environ.get('HOME') diff --git a/test/test_config.py b/test/test_config.py index a393b65..238ca66 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -71,7 +71,7 @@ def _generate_expected_groups(): Path('/etc/yt-dlp.conf'), Path('/etc/yt-dlp/config'), Path('/etc/yt-dlp/config.txt'), - ] + ], } diff --git a/test/test_cookies.py b/test/test_cookies.py index bd61f30..e1271f6 100644 --- a/test/test_cookies.py +++ b/test/test_cookies.py @@ -67,6 +67,7 @@ class TestCookies(unittest.TestCase): ({'XDG_CURRENT_DESKTOP': 'GNOME'}, _LinuxDesktopEnvironment.GNOME), ({'XDG_CURRENT_DESKTOP': 'GNOME:GNOME-Classic'}, _LinuxDesktopEnvironment.GNOME), ({'XDG_CURRENT_DESKTOP': 'GNOME : GNOME-Classic'}, _LinuxDesktopEnvironment.GNOME), + ({'XDG_CURRENT_DESKTOP': 'ubuntu:GNOME'}, _LinuxDesktopEnvironment.GNOME), ({'XDG_CURRENT_DESKTOP': 'Unity', 'DESKTOP_SESSION': 'gnome-fallback'}, _LinuxDesktopEnvironment.GNOME), ({'XDG_CURRENT_DESKTOP': 'KDE', 'KDE_SESSION_VERSION': '5'}, _LinuxDesktopEnvironment.KDE5), @@ -106,7 +107,7 @@ class TestCookies(unittest.TestCase): def test_chrome_cookie_decryptor_windows_v10(self): with MonkeyPatch(cookies, { - '_get_windows_v10_key': lambda *args, **kwargs: b'Y\xef\xad\xad\xeerp\xf0Y\xe6\x9b\x12\xc2<z\x16]\n\xbb\xb8\xcb\xd7\x9bA\xc3\x14e\x99{\xd6\xf4&' + '_get_windows_v10_key': lambda *args, **kwargs: b'Y\xef\xad\xad\xeerp\xf0Y\xe6\x9b\x12\xc2<z\x16]\n\xbb\xb8\xcb\xd7\x9bA\xc3\x14e\x99{\xd6\xf4&', }): encrypted_value = b'v10T\xb8\xf3\xb8\x01\xa7TtcV\xfc\x88\xb8\xb8\xef\x05\xb5\xfd\x18\xc90\x009\xab\xb1\x893\x85)\x87\xe1\xa9-\xa3\xad=' value = '32101439' @@ -121,17 +122,17 @@ class TestCookies(unittest.TestCase): self.assertEqual(decryptor.decrypt(encrypted_value), value) def test_safari_cookie_parsing(self): - cookies = \ - b'cook\x00\x00\x00\x01\x00\x00\x00i\x00\x00\x01\x00\x01\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00Y' \ - b'\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x008\x00\x00\x00B\x00\x00\x00F\x00\x00\x00H' \ - b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x03\xa5>\xc3A\x00\x00\x80\xc3\x07:\xc3A' \ - b'localhost\x00foo\x00/\x00test%20%3Bcookie\x00\x00\x00\x054\x07\x17 \x05\x00\x00\x00Kbplist00\xd1\x01' \ - b'\x02_\x10\x18NSHTTPCookieAcceptPolicy\x10\x02\x08\x0b&\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00' \ - b'\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(' + cookies = ( + b'cook\x00\x00\x00\x01\x00\x00\x00i\x00\x00\x01\x00\x01\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00Y' + b'\x00\x00\x00\x00\x00\x00\x00 \x00\x00\x00\x00\x00\x00\x008\x00\x00\x00B\x00\x00\x00F\x00\x00\x00H' + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x03\xa5>\xc3A\x00\x00\x80\xc3\x07:\xc3A' + b'localhost\x00foo\x00/\x00test%20%3Bcookie\x00\x00\x00\x054\x07\x17 \x05\x00\x00\x00Kbplist00\xd1\x01' + b'\x02_\x10\x18NSHTTPCookieAcceptPolicy\x10\x02\x08\x0b&\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00' + b'\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00(') jar = parse_safari_cookies(cookies) self.assertEqual(len(jar), 1) - cookie = list(jar)[0] + cookie = next(iter(jar)) self.assertEqual(cookie.domain, 'localhost') self.assertEqual(cookie.port, None) self.assertEqual(cookie.path, '/') @@ -164,7 +165,7 @@ class TestLenientSimpleCookie(unittest.TestCase): attributes = { key: value for key, value in dict(morsel).items() - if value != "" + if value != '' } self.assertEqual(attributes, expected_attributes, message) @@ -174,133 +175,133 @@ class TestLenientSimpleCookie(unittest.TestCase): self._run_tests( # Copied from https://github.com/python/cpython/blob/v3.10.7/Lib/test/test_http_cookies.py ( - "Test basic cookie", - "chips=ahoy; vienna=finger", - {"chips": "ahoy", "vienna": "finger"}, + 'Test basic cookie', + 'chips=ahoy; vienna=finger', + {'chips': 'ahoy', 'vienna': 'finger'}, ), ( - "Test quoted cookie", + 'Test quoted cookie', 'keebler="E=mc2; L=\\"Loves\\"; fudge=\\012;"', - {"keebler": 'E=mc2; L="Loves"; fudge=\012;'}, + {'keebler': 'E=mc2; L="Loves"; fudge=\012;'}, ), ( "Allow '=' in an unquoted value", - "keebler=E=mc2", - {"keebler": "E=mc2"}, + 'keebler=E=mc2', + {'keebler': 'E=mc2'}, ), ( "Allow cookies with ':' in their name", - "key:term=value:term", - {"key:term": "value:term"}, + 'key:term=value:term', + {'key:term': 'value:term'}, ), ( "Allow '[' and ']' in cookie values", - "a=b; c=[; d=r; f=h", - {"a": "b", "c": "[", "d": "r", "f": "h"}, + 'a=b; c=[; d=r; f=h', + {'a': 'b', 'c': '[', 'd': 'r', 'f': 'h'}, ), ( - "Test basic cookie attributes", + 'Test basic cookie attributes', 'Customer="WILE_E_COYOTE"; Version=1; Path=/acme', - {"Customer": ("WILE_E_COYOTE", {"version": "1", "path": "/acme"})}, + {'Customer': ('WILE_E_COYOTE', {'version': '1', 'path': '/acme'})}, ), ( - "Test flag only cookie attributes", + 'Test flag only cookie attributes', 'Customer="WILE_E_COYOTE"; HttpOnly; Secure', - {"Customer": ("WILE_E_COYOTE", {"httponly": True, "secure": True})}, + {'Customer': ('WILE_E_COYOTE', {'httponly': True, 'secure': True})}, ), ( - "Test flag only attribute with values", - "eggs=scrambled; httponly=foo; secure=bar; Path=/bacon", - {"eggs": ("scrambled", {"httponly": "foo", "secure": "bar", "path": "/bacon"})}, + 'Test flag only attribute with values', + 'eggs=scrambled; httponly=foo; secure=bar; Path=/bacon', + {'eggs': ('scrambled', {'httponly': 'foo', 'secure': 'bar', 'path': '/bacon'})}, ), ( "Test special case for 'expires' attribute, 4 digit year", 'Customer="W"; expires=Wed, 01 Jan 2010 00:00:00 GMT', - {"Customer": ("W", {"expires": "Wed, 01 Jan 2010 00:00:00 GMT"})}, + {'Customer': ('W', {'expires': 'Wed, 01 Jan 2010 00:00:00 GMT'})}, ), ( "Test special case for 'expires' attribute, 2 digit year", 'Customer="W"; expires=Wed, 01 Jan 98 00:00:00 GMT', - {"Customer": ("W", {"expires": "Wed, 01 Jan 98 00:00:00 GMT"})}, + {'Customer': ('W', {'expires': 'Wed, 01 Jan 98 00:00:00 GMT'})}, ), ( - "Test extra spaces in keys and values", - "eggs = scrambled ; secure ; path = bar ; foo=foo ", - {"eggs": ("scrambled", {"secure": True, "path": "bar"}), "foo": "foo"}, + 'Test extra spaces in keys and values', + 'eggs = scrambled ; secure ; path = bar ; foo=foo ', + {'eggs': ('scrambled', {'secure': True, 'path': 'bar'}), 'foo': 'foo'}, ), ( - "Test quoted attributes", + 'Test quoted attributes', 'Customer="WILE_E_COYOTE"; Version="1"; Path="/acme"', - {"Customer": ("WILE_E_COYOTE", {"version": "1", "path": "/acme"})} + {'Customer': ('WILE_E_COYOTE', {'version': '1', 'path': '/acme'})}, ), # Our own tests that CPython passes ( "Allow ';' in quoted value", 'chips="a;hoy"; vienna=finger', - {"chips": "a;hoy", "vienna": "finger"}, + {'chips': 'a;hoy', 'vienna': 'finger'}, ), ( - "Keep only the last set value", - "a=c; a=b", - {"a": "b"}, + 'Keep only the last set value', + 'a=c; a=b', + {'a': 'b'}, ), ) def test_lenient_parsing(self): self._run_tests( ( - "Ignore and try to skip invalid cookies", + 'Ignore and try to skip invalid cookies', 'chips={"ahoy;": 1}; vienna="finger;"', - {"vienna": "finger;"}, + {'vienna': 'finger;'}, ), ( - "Ignore cookies without a name", - "a=b; unnamed; c=d", - {"a": "b", "c": "d"}, + 'Ignore cookies without a name', + 'a=b; unnamed; c=d', + {'a': 'b', 'c': 'd'}, ), ( "Ignore '\"' cookie without name", 'a=b; "; c=d', - {"a": "b", "c": "d"}, + {'a': 'b', 'c': 'd'}, ), ( - "Skip all space separated values", - "x a=b c=d x; e=f", - {"a": "b", "c": "d", "e": "f"}, + 'Skip all space separated values', + 'x a=b c=d x; e=f', + {'a': 'b', 'c': 'd', 'e': 'f'}, ), ( - "Skip all space separated values", + 'Skip all space separated values', 'x a=b; data={"complex": "json", "with": "key=value"}; x c=d x', - {"a": "b", "c": "d"}, + {'a': 'b', 'c': 'd'}, ), ( - "Expect quote mending", + 'Expect quote mending', 'a=b; invalid="; c=d', - {"a": "b", "c": "d"}, + {'a': 'b', 'c': 'd'}, ), ( - "Reset morsel after invalid to not capture attributes", - "a=b; invalid; Version=1; c=d", - {"a": "b", "c": "d"}, + 'Reset morsel after invalid to not capture attributes', + 'a=b; invalid; Version=1; c=d', + {'a': 'b', 'c': 'd'}, ), ( - "Reset morsel after invalid to not capture attributes", - "a=b; $invalid; $Version=1; c=d", - {"a": "b", "c": "d"}, + 'Reset morsel after invalid to not capture attributes', + 'a=b; $invalid; $Version=1; c=d', + {'a': 'b', 'c': 'd'}, ), ( - "Continue after non-flag attribute without value", - "a=b; path; Version=1; c=d", - {"a": "b", "c": "d"}, + 'Continue after non-flag attribute without value', + 'a=b; path; Version=1; c=d', + {'a': 'b', 'c': 'd'}, ), ( - "Allow cookie attributes with `$` prefix", + 'Allow cookie attributes with `$` prefix', 'Customer="WILE_E_COYOTE"; $Version=1; $Secure; $Path=/acme', - {"Customer": ("WILE_E_COYOTE", {"version": "1", "secure": True, "path": "/acme"})}, + {'Customer': ('WILE_E_COYOTE', {'version': '1', 'secure': True, 'path': '/acme'})}, ), ( - "Invalid Morsel keys should not result in an error", - "Key=Value; [Invalid]=Value; Another=Value", - {"Key": "Value", "Another": "Value"}, + 'Invalid Morsel keys should not result in an error', + 'Key=Value; [Invalid]=Value; Another=Value', + {'Key': 'Value', 'Another': 'Value'}, ), ) diff --git a/test/test_download.py b/test/test_download.py index 2530792..3f36869 100755 --- a/test/test_download.py +++ b/test/test_download.py @@ -20,7 +20,6 @@ from test.helper import ( gettestcases, getwebpagetestcases, is_download_test, - report_warning, try_rm, ) @@ -94,7 +93,7 @@ def generator(test_case, tname): 'playlist', [] if is_playlist else [test_case]) def print_skipping(reason): - print('Skipping %s: %s' % (test_case['name'], reason)) + print('Skipping {}: {}'.format(test_case['name'], reason)) self.skipTest(reason) if not ie.working(): @@ -117,7 +116,7 @@ def generator(test_case, tname): for other_ie in other_ies: if not other_ie.working(): - print_skipping('test depends on %sIE, marked as not WORKING' % other_ie.ie_key()) + print_skipping(f'test depends on {other_ie.ie_key()}IE, marked as not WORKING') params = get_params(test_case.get('params', {})) params['outtmpl'] = tname + '_' + params['outtmpl'] @@ -148,10 +147,7 @@ def generator(test_case, tname): return False if err.__class__.__name__ == expected_exception: return True - for exc in err.exc_info: - if exc.__class__.__name__ == expected_exception: - return True - return False + return any(exc.__class__.__name__ == expected_exception for exc in err.exc_info) def try_rm_tcs_files(tcs=None): if tcs is None: @@ -181,8 +177,7 @@ def generator(test_case, tname): raise if try_num == RETRIES: - report_warning('%s failed due to network errors, skipping...' % tname) - return + raise print(f'Retrying: {try_num} failed tries\n\n##########\n\n') @@ -244,9 +239,8 @@ def generator(test_case, tname): got_fsize = os.path.getsize(tc_filename) assertGreaterEqual( self, got_fsize, expected_minsize, - 'Expected %s to be at least %s, but it\'s only %s ' % - (tc_filename, format_bytes(expected_minsize), - format_bytes(got_fsize))) + 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: md5_for_file = _file_md5(tc_filename) self.assertEqual(tc['md5'], md5_for_file) @@ -255,7 +249,7 @@ def generator(test_case, tname): info_json_fn = os.path.splitext(tc_filename)[0] + '.info.json' self.assertTrue( os.path.exists(info_json_fn), - 'Missing info file %s' % info_json_fn) + f'Missing info file {info_json_fn}') with open(info_json_fn, encoding='utf-8') as infof: info_dict = json.load(infof) expect_info_dict(self, info_dict, tc.get('info_dict', {})) diff --git a/test/test_downloader_http.py b/test/test_downloader_http.py index 099ec2f..faba0bc 100644 --- a/test/test_downloader_http.py +++ b/test/test_downloader_http.py @@ -38,9 +38,9 @@ class HTTPTestRequestHandler(http.server.BaseHTTPRequestHandler): end = int(mobj.group(2)) valid_range = start is not None and end is not None if valid_range: - content_range = 'bytes %d-%d' % (start, end) + content_range = f'bytes {start}-{end}' if total: - content_range += '/%d' % total + content_range += f'/{total}' self.send_header('Content-Range', content_range) return (end - start + 1) if valid_range else total @@ -84,7 +84,7 @@ class TestHttpFD(unittest.TestCase): filename = 'testfile.mp4' try_rm(encodeFilename(filename)) self.assertTrue(downloader.real_download(filename, { - 'url': 'http://127.0.0.1:%d/%s' % (self.port, ep), + 'url': f'http://127.0.0.1:{self.port}/{ep}', }), ep) self.assertEqual(os.path.getsize(encodeFilename(filename)), TEST_SIZE, ep) try_rm(encodeFilename(filename)) diff --git a/test/test_http_proxy.py b/test/test_http_proxy.py index 1b21fe7..2435c87 100644 --- a/test/test_http_proxy.py +++ b/test/test_http_proxy.py @@ -105,7 +105,7 @@ if urllib3: self.incoming, self.outgoing, server_hostname=server_hostname, - server_side=server_side + server_side=server_side, ) self._ssl_io_loop(self.sslobj.do_handshake) @@ -333,7 +333,7 @@ class TestHTTPConnectProxy: @pytest.mark.skip_handler( 'Requests', - 'bug in urllib3 causes unclosed socket: https://github.com/urllib3/urllib3/issues/3374' + 'bug in urllib3 causes unclosed socket: https://github.com/urllib3/urllib3/issues/3374', ) def test_http_connect_bad_auth(self, handler, ctx): with ctx.http_server(HTTPConnectProxyHandler, username='test', password='test') as server_address: diff --git a/test/test_iqiyi_sdk_interpreter.py b/test/test_iqiyi_sdk_interpreter.py index 47c632a..4e41007 100644 --- a/test/test_iqiyi_sdk_interpreter.py +++ b/test/test_iqiyi_sdk_interpreter.py @@ -29,11 +29,11 @@ class WarningLogger: @is_download_test class TestIqiyiSDKInterpreter(unittest.TestCase): def test_iqiyi_sdk_interpreter(self): - ''' + """ Test the functionality of IqiyiSDKInterpreter by trying to log in If `sign` is incorrect, /validate call throws an HTTP 556 error - ''' + """ logger = WarningLogger() ie = IqiyiIE(FakeYDL({'logger': logger})) ie._perform_login('foo', 'bar') diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py index 86928a6..7c556e4 100644 --- a/test/test_jsinterp.py +++ b/test/test_jsinterp.py @@ -92,6 +92,7 @@ class TestJSInterpreter(unittest.TestCase): self._test('function f(){return 0 && 1 || 2;}', 2) self._test('function f(){return 0 ?? 42;}', 0) self._test('function f(){return "life, the universe and everything" < 42;}', False) + self._test('function f(){return 0 - 7 * - 6;}', 42) def test_array_access(self): self._test('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2.0] = 7; return x;}', [5, 2, 7]) diff --git a/test/test_netrc.py b/test/test_netrc.py index dc708d9..1e0f4ee 100644 --- a/test/test_netrc.py +++ b/test/test_netrc.py @@ -21,7 +21,7 @@ class TestNetRc(unittest.TestCase): continue self.assertTrue( ie._NETRC_MACHINE, - 'Extractor %s supports login, but is missing a _NETRC_MACHINE property' % ie.IE_NAME) + f'Extractor {ie.IE_NAME} supports login, but is missing a _NETRC_MACHINE property') if __name__ == '__main__': diff --git a/test/test_networking.py b/test/test_networking.py index d127cbb..af3ece3 100644 --- a/test/test_networking.py +++ b/test/test_networking.py @@ -375,10 +375,10 @@ class TestHTTPRequestHandler(TestRequestHandlerBase): with handler() as rh: for bad_status in (400, 500, 599, 302): with pytest.raises(HTTPError): - validate_and_send(rh, Request('http://127.0.0.1:%d/gen_%d' % (self.http_port, bad_status))) + validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/gen_{bad_status}')) # Should not raise an error - validate_and_send(rh, Request('http://127.0.0.1:%d/gen_200' % self.http_port)).close() + validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/gen_200')).close() def test_response_url(self, handler): with handler() as rh: @@ -472,7 +472,7 @@ class TestHTTPRequestHandler(TestRequestHandlerBase): def test_incompleteread(self, handler): with handler(timeout=2) as rh: with pytest.raises(IncompleteRead, match='13 bytes read, 234221 more expected'): - validate_and_send(rh, Request('http://127.0.0.1:%d/incompleteread' % self.http_port)).read() + validate_and_send(rh, Request(f'http://127.0.0.1:{self.http_port}/incompleteread')).read() def test_cookies(self, handler): cookiejar = YoutubeDLCookieJar() @@ -740,7 +740,7 @@ class TestRequestHandlerMisc: @pytest.mark.parametrize('handler,logger_name', [ ('Requests', 'urllib3'), ('Websockets', 'websockets.client'), - ('Websockets', 'websockets.server') + ('Websockets', 'websockets.server'), ], indirect=['handler']) def test_remove_logging_handler(self, handler, logger_name): # Ensure any logging handlers, which may contain a YoutubeDL instance, @@ -794,7 +794,7 @@ class TestUrllibRequestHandler(TestRequestHandlerBase): with handler() as rh: with pytest.raises( CertificateVerifyError, - match=r'\[SSL: CERTIFICATE_VERIFY_FAILED\] certificate verify failed: self.signed certificate' + match=r'\[SSL: CERTIFICATE_VERIFY_FAILED\] certificate verify failed: self.signed certificate', ): validate_and_send(rh, Request(f'https://127.0.0.1:{self.https_port}/headers')) @@ -804,14 +804,14 @@ class TestUrllibRequestHandler(TestRequestHandlerBase): ( 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) + lambda v: v < (3, 7, 9) or (3, 8, 0) <= v < (3, 8, 5), ), # 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) + lambda v: v < (3, 7, 8) or (3, 8, 0) <= v < (3, 8, 3), ), # 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), @@ -840,7 +840,7 @@ class TestRequestsRequestHandler(TestRequestHandlerBase): (lambda: requests.exceptions.InvalidHeader(), RequestError), # catch-all: https://github.com/psf/requests/blob/main/src/requests/adapters.py#L535 (lambda: urllib3.exceptions.HTTPError(), TransportError), - (lambda: requests.exceptions.RequestException(), RequestError) + (lambda: requests.exceptions.RequestException(), RequestError), # (lambda: requests.exceptions.TooManyRedirects(), HTTPError) - Needs a response object ]) def test_request_error_mapping(self, handler, monkeypatch, raised, expected): @@ -868,12 +868,12 @@ class TestRequestsRequestHandler(TestRequestHandlerBase): ( lambda: urllib3.exceptions.ProtocolError('error', http.client.IncompleteRead(partial=b'abc', expected=4)), IncompleteRead, - '3 bytes read, 4 more expected' + '3 bytes read, 4 more expected', ), ( lambda: urllib3.exceptions.ProtocolError('error', urllib3.exceptions.IncompleteRead(partial=3, expected=5)), IncompleteRead, - '3 bytes read, 5 more expected' + '3 bytes read, 5 more expected', ), ]) def test_response_error_mapping(self, handler, monkeypatch, raised, expected, match): @@ -1125,7 +1125,7 @@ class TestRequestHandlerValidation: ('https', False, {}), ]), (NoCheckRH, [('http', False, {})]), - (ValidationRH, [('http', UnsupportedRequest, {})]) + (ValidationRH, [('http', UnsupportedRequest, {})]), ] PROXY_SCHEME_TESTS = [ @@ -1219,7 +1219,7 @@ class TestRequestHandlerValidation: ({'impersonate': ImpersonateTarget('chrome', None, None, None)}, False), ({'impersonate': ImpersonateTarget(None, None, None, None)}, False), ({'impersonate': ImpersonateTarget()}, False), - ({'impersonate': 'chrome'}, AssertionError) + ({'impersonate': 'chrome'}, AssertionError), ]), (NoCheckRH, 'http', [ ({'cookiejar': 'notacookiejar'}, False), @@ -1235,7 +1235,7 @@ class TestRequestHandlerValidation: ('Urllib', False, 'http'), ('Requests', False, 'http'), ('CurlCFFI', False, 'http'), - ('Websockets', False, 'ws') + ('Websockets', False, 'ws'), ], indirect=['handler']) def test_no_proxy(self, handler, fail, scheme): run_validation(handler, fail, Request(f'{scheme}://', proxies={'no': '127.0.0.1,github.com'})) @@ -1246,7 +1246,7 @@ class TestRequestHandlerValidation: (HTTPSupportedRH, 'http'), ('Requests', 'http'), ('CurlCFFI', 'http'), - ('Websockets', 'ws') + ('Websockets', 'ws'), ], indirect=['handler']) def test_empty_proxy(self, handler, scheme): run_validation(handler, False, Request(f'{scheme}://', proxies={scheme: None})) @@ -1258,7 +1258,7 @@ class TestRequestHandlerValidation: (HTTPSupportedRH, 'http'), ('Requests', 'http'), ('CurlCFFI', 'http'), - ('Websockets', 'ws') + ('Websockets', 'ws'), ], indirect=['handler']) def test_invalid_proxy_url(self, handler, scheme, proxy_url): run_validation(handler, UnsupportedRequest, Request(f'{scheme}://', proxies={scheme: proxy_url})) @@ -1474,7 +1474,7 @@ class TestYoutubeDLNetworking: @pytest.mark.parametrize('proxy,expected', [ ('http://127.0.0.1:8080', {'all': 'http://127.0.0.1:8080'}), ('', {'all': '__noproxy__'}), - (None, {'http': 'http://127.0.0.1:8081', 'https': 'http://127.0.0.1:8081'}) # env, set https + (None, {'http': 'http://127.0.0.1:8081', 'https': 'http://127.0.0.1:8081'}), # env, set https ]) def test_proxy(self, proxy, expected, monkeypatch): monkeypatch.setenv('HTTP_PROXY', 'http://127.0.0.1:8081') @@ -1546,7 +1546,7 @@ class TestYoutubeDLNetworking: with FakeImpersonationRHYDL() as ydl: with pytest.raises( RequestError, - match=r'Impersonate target "test" is not available' + match=r'Impersonate target "test" is not available', ): ydl.urlopen(Request('http://', extensions={'impersonate': ImpersonateTarget('test', None, None, None)})) @@ -1558,7 +1558,7 @@ class TestYoutubeDLNetworking: pass _SUPPORTED_URL_SCHEMES = ('http',) - _SUPPORTED_IMPERSONATE_TARGET_MAP = {ImpersonateTarget('abc',): 'test'} + _SUPPORTED_IMPERSONATE_TARGET_MAP = {ImpersonateTarget('abc'): 'test'} _SUPPORTED_PROXY_SCHEMES = None super().__init__(*args, **kwargs) @@ -1567,14 +1567,14 @@ class TestYoutubeDLNetworking: with FakeHTTPRHYDL() as ydl: with pytest.raises( RequestError, - match=r'Impersonate target "test" is not available' + match=r'Impersonate target "test" is not available', ): ydl.urlopen(Request('http://', extensions={'impersonate': ImpersonateTarget('test', None, None, None)})) def test_raise_impersonate_error(self): with pytest.raises( YoutubeDLError, - match=r'Impersonate target "test" is not available' + match=r'Impersonate target "test" is not available', ): FakeYDL({'impersonate': ImpersonateTarget('test', None, None, None)}) @@ -1592,7 +1592,7 @@ class TestYoutubeDLNetworking: monkeypatch.setattr(FakeYDL, 'build_request_director', lambda cls, handlers, preferences=None: brh(cls, handlers=[IRH])) with FakeYDL({ - 'impersonate': ImpersonateTarget('abc', None, None, None) + 'impersonate': ImpersonateTarget('abc', None, None, None), }) as ydl: rh = self.build_handler(ydl, IRH) assert rh.impersonate == ImpersonateTarget('abc', None, None, None) @@ -1604,7 +1604,7 @@ class TestYoutubeDLNetworking: def _send(self, request: Request): pass _SUPPORTED_URL_SCHEMES = ('http',) - _SUPPORTED_IMPERSONATE_TARGET_MAP = {ImpersonateTarget(target_client,): 'test'} + _SUPPORTED_IMPERSONATE_TARGET_MAP = {ImpersonateTarget(target_client): 'test'} RH_KEY = target_client RH_NAME = target_client handlers.append(TestRH) @@ -1614,7 +1614,7 @@ class TestYoutubeDLNetworking: assert set(ydl._get_available_impersonate_targets()) == { (ImpersonateTarget('xyz'), 'xyz'), (ImpersonateTarget('abc'), 'abc'), - (ImpersonateTarget('asd'), 'asd') + (ImpersonateTarget('asd'), 'asd'), } assert ydl._impersonate_target_available(ImpersonateTarget('abc')) assert ydl._impersonate_target_available(ImpersonateTarget()) @@ -1837,7 +1837,7 @@ class TestRequest: extensions={'cookiejar': CookieJar()}, headers={'Accept-Encoding': 'br'}, proxies={'http': 'http://127.0.0.1'}, - data=[b'123'] + data=[b'123'], ) req_copy = req.copy() assert req_copy is not req @@ -1863,7 +1863,7 @@ class TestRequest: assert isinstance(req.copy(), AnotherRequest) def test_url(self): - req = Request(url='https://фtest.example.com/ some spaceв?ä=c',) + req = Request(url='https://фtest.example.com/ some spaceв?ä=c') assert req.url == 'https://xn--test-z6d.example.com/%20some%20space%D0%B2?%C3%A4=c' assert Request(url='//example.com').url == 'http://example.com' @@ -1878,7 +1878,7 @@ class TestResponse: ('custom', 200, 'custom'), (None, 404, 'Not Found'), # fallback status ('', 403, 'Forbidden'), - (None, 999, None) + (None, 999, None), ]) def test_reason(self, reason, status, expected): res = Response(io.BytesIO(b''), url='test://', headers={}, status=status, reason=reason) @@ -1933,7 +1933,7 @@ class TestImpersonateTarget: @pytest.mark.parametrize('target_str', [ '-120', ':-12.0', '-12:-12', '-:-', - '::', 'a-c-d:', 'a-c-d:e-f-g', 'a:b:' + '::', 'a-c-d:', 'a-c-d:e-f-g', 'a:b:', ]) def test_target_from_invalid_str(self, target_str): with pytest.raises(ValueError): @@ -1949,7 +1949,7 @@ class TestImpersonateTarget: (ImpersonateTarget('abc', '120', 'xyz', None), 'abc-120:xyz'), (ImpersonateTarget('abc', None, 'xyz'), 'abc:xyz'), (ImpersonateTarget(None, None, 'xyz', '6.5'), ':xyz-6.5'), - (ImpersonateTarget('abc', ), 'abc'), + (ImpersonateTarget('abc'), 'abc'), (ImpersonateTarget(None, None, None, None), ''), ]) def test_str(self, target, expected): diff --git a/test/test_networking_utils.py b/test/test_networking_utils.py index b7b7143..204fe87 100644 --- a/test/test_networking_utils.py +++ b/test/test_networking_utils.py @@ -39,7 +39,7 @@ class TestNetworkingUtils: proxies = { 'all': 'socks5://example.com', 'http': 'http://example.com:1080', - 'no': 'bypass.example.com,yt-dl.org' + 'no': 'bypass.example.com,yt-dl.org', } assert select_proxy('https://example.com', proxies) == proxies['all'] @@ -54,7 +54,7 @@ class TestNetworkingUtils: 'port': 1080, 'rdns': True, 'username': None, - 'password': None + 'password': None, }), ('socks5://user:@example.com:5555', { 'proxytype': ProxyType.SOCKS5, @@ -62,7 +62,7 @@ class TestNetworkingUtils: 'port': 5555, 'rdns': False, 'username': 'user', - 'password': '' + 'password': '', }), ('socks4://u%40ser:pa%20ss@127.0.0.1:1080', { 'proxytype': ProxyType.SOCKS4, @@ -70,7 +70,7 @@ class TestNetworkingUtils: 'port': 1080, 'rdns': False, 'username': 'u@ser', - 'password': 'pa ss' + 'password': 'pa ss', }), ('socks4a://:pa%20ss@127.0.0.1', { 'proxytype': ProxyType.SOCKS4A, @@ -78,8 +78,8 @@ class TestNetworkingUtils: 'port': 1080, 'rdns': True, 'username': '', - 'password': 'pa ss' - }) + 'password': 'pa ss', + }), ]) def test_make_socks_proxy_opts(self, socks_proxy, expected): assert make_socks_proxy_opts(socks_proxy) == expected diff --git a/test/test_overwrites.py b/test/test_overwrites.py index 6954c07..0beafdf 100644 --- a/test/test_overwrites.py +++ b/test/test_overwrites.py @@ -27,7 +27,7 @@ class TestOverwrites(unittest.TestCase): [ sys.executable, 'yt_dlp/__main__.py', '-o', 'test.webm', - 'https://www.youtube.com/watch?v=jNQXAC9IVRw' + 'https://www.youtube.com/watch?v=jNQXAC9IVRw', ], cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sout, serr = outp.communicate() self.assertTrue(b'has already been downloaded' in sout) @@ -39,7 +39,7 @@ class TestOverwrites(unittest.TestCase): [ sys.executable, 'yt_dlp/__main__.py', '--yes-overwrites', '-o', 'test.webm', - 'https://www.youtube.com/watch?v=jNQXAC9IVRw' + 'https://www.youtube.com/watch?v=jNQXAC9IVRw', ], cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) sout, serr = outp.communicate() self.assertTrue(b'has already been downloaded' not in sout) diff --git a/test/test_plugins.py b/test/test_plugins.py index 6cde579..c82158e 100644 --- a/test/test_plugins.py +++ b/test/test_plugins.py @@ -31,7 +31,7 @@ class TestPlugins(unittest.TestCase): # don't load modules with underscore prefix self.assertFalse( - f'{PACKAGE_NAME}.extractor._ignore' in sys.modules.keys(), + f'{PACKAGE_NAME}.extractor._ignore' in sys.modules, 'loaded module beginning with underscore') self.assertNotIn('IgnorePluginIE', plugins_ie.keys()) diff --git a/test/test_post_hooks.py b/test/test_post_hooks.py index 3778d17..6500dd3 100644 --- a/test/test_post_hooks.py +++ b/test/test_post_hooks.py @@ -59,7 +59,7 @@ class TestPostHooks(unittest.TestCase): def hook_three(self, filename): self.files.append(filename) - raise Exception('Test exception for \'%s\'' % filename) + raise Exception(f'Test exception for \'{filename}\'') def tearDown(self): for f in self.files: diff --git a/test/test_postprocessors.py b/test/test_postprocessors.py index 52e5587..603f85c 100644 --- a/test/test_postprocessors.py +++ b/test/test_postprocessors.py @@ -9,7 +9,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from yt_dlp import YoutubeDL -from yt_dlp.compat import compat_shlex_quote +from yt_dlp.utils import shell_quote from yt_dlp.postprocessor import ( ExecPP, FFmpegThumbnailsConvertorPP, @@ -65,7 +65,7 @@ class TestExec(unittest.TestCase): def test_parse_cmd(self): pp = ExecPP(YoutubeDL(), '') info = {'filepath': 'file name'} - cmd = 'echo %s' % compat_shlex_quote(info['filepath']) + cmd = 'echo {}'.format(shell_quote(info['filepath'])) self.assertEqual(pp.parse_cmd('echo', info), cmd) self.assertEqual(pp.parse_cmd('echo {}', info), cmd) @@ -125,7 +125,8 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, chapters, []) def test_remove_marked_arrange_sponsors_ChapterWithSponsors(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 20, 'sponsor'), self._sponsor_chapter(30, 40, 'preview'), self._sponsor_chapter(50, 60, 'filler')] @@ -136,7 +137,8 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) def test_remove_marked_arrange_sponsors_SponsorBlockChapters(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 20, 'chapter', title='sb c1'), self._sponsor_chapter(15, 16, 'chapter', title='sb c2'), self._sponsor_chapter(30, 40, 'preview'), @@ -149,10 +151,14 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) def test_remove_marked_arrange_sponsors_UniqueNamesForOverlappingSponsors(self): - chapters = self._chapters([120], ['c']) + [ - self._sponsor_chapter(10, 45, 'sponsor'), self._sponsor_chapter(20, 40, 'selfpromo'), - self._sponsor_chapter(50, 70, 'sponsor'), self._sponsor_chapter(60, 85, 'selfpromo'), - self._sponsor_chapter(90, 120, 'selfpromo'), self._sponsor_chapter(100, 110, 'sponsor')] + chapters = [ + *self._chapters([120], ['c']), + self._sponsor_chapter(10, 45, 'sponsor'), + self._sponsor_chapter(20, 40, 'selfpromo'), + self._sponsor_chapter(50, 70, 'sponsor'), + self._sponsor_chapter(60, 85, 'selfpromo'), + self._sponsor_chapter(90, 120, 'selfpromo'), + self._sponsor_chapter(100, 110, 'sponsor')] expected = self._chapters( [10, 20, 40, 45, 50, 60, 70, 85, 90, 100, 110, 120], ['c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Sponsor, Unpaid/Self Promotion', @@ -172,7 +178,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, self._chapters([40], ['c']), cuts) def test_remove_marked_arrange_sponsors_ChapterWithSponsorsAndCuts(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 20, 'sponsor'), self._sponsor_chapter(30, 40, 'selfpromo', remove=True), self._sponsor_chapter(50, 60, 'interaction')] @@ -185,24 +192,29 @@ class TestModifyChaptersPP(unittest.TestCase): def test_remove_marked_arrange_sponsors_ChapterWithSponsorCutInTheMiddle(self): cuts = [self._sponsor_chapter(20, 30, 'selfpromo', remove=True), self._chapter(40, 50, remove=True)] - chapters = self._chapters([70], ['c']) + [self._sponsor_chapter(10, 60, 'sponsor')] + cuts + chapters = [ + *self._chapters([70], ['c']), + self._sponsor_chapter(10, 60, 'sponsor'), + *cuts] expected = self._chapters( [10, 40, 50], ['c', '[SponsorBlock]: Sponsor', 'c']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_ChapterWithCutHidingSponsor(self): cuts = [self._sponsor_chapter(20, 50, 'selfpromo', remove=True)] - chapters = self._chapters([60], ['c']) + [ + chapters = [ + *self._chapters([60], ['c']), self._sponsor_chapter(10, 20, 'intro'), self._sponsor_chapter(30, 40, 'sponsor'), self._sponsor_chapter(50, 60, 'outro'), - ] + cuts + *cuts] expected = self._chapters( [10, 20, 30], ['c', '[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_ChapterWithAdjacentSponsors(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 20, 'sponsor'), self._sponsor_chapter(20, 30, 'selfpromo'), self._sponsor_chapter(30, 40, 'interaction')] @@ -213,7 +225,8 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) def test_remove_marked_arrange_sponsors_ChapterWithAdjacentCuts(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 20, 'sponsor'), self._sponsor_chapter(20, 30, 'interaction', remove=True), self._chapter(30, 40, remove=True), @@ -226,7 +239,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, expected, [self._chapter(20, 50, remove=True)]) def test_remove_marked_arrange_sponsors_ChapterWithOverlappingSponsors(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 30, 'sponsor'), self._sponsor_chapter(20, 50, 'selfpromo'), self._sponsor_chapter(40, 60, 'interaction')] @@ -238,7 +252,8 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) def test_remove_marked_arrange_sponsors_ChapterWithOverlappingCuts(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 30, 'sponsor', remove=True), self._sponsor_chapter(20, 50, 'selfpromo', remove=True), self._sponsor_chapter(40, 60, 'interaction', remove=True)] @@ -246,7 +261,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, self._chapters([20], ['c']), [self._chapter(10, 60, remove=True)]) def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsors(self): - chapters = self._chapters([170], ['c']) + [ + chapters = [ + *self._chapters([170], ['c']), self._sponsor_chapter(0, 30, 'intro'), self._sponsor_chapter(20, 50, 'sponsor'), self._sponsor_chapter(40, 60, 'selfpromo'), @@ -267,7 +283,8 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingCuts(self): - chapters = self._chapters([170], ['c']) + [ + chapters = [ + *self._chapters([170], ['c']), self._chapter(0, 30, remove=True), self._sponsor_chapter(20, 50, 'sponsor', remove=True), self._chapter(40, 60, remove=True), @@ -284,7 +301,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, self._chapters([20], ['c']), expected_cuts) def test_remove_marked_arrange_sponsors_OverlappingSponsorsDifferentTitlesAfterCut(self): - chapters = self._chapters([60], ['c']) + [ + chapters = [ + *self._chapters([60], ['c']), self._sponsor_chapter(10, 60, 'sponsor'), self._sponsor_chapter(10, 40, 'intro'), self._sponsor_chapter(30, 50, 'interaction'), @@ -297,7 +315,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, expected, [self._chapter(30, 50, remove=True)]) def test_remove_marked_arrange_sponsors_SponsorsNoLongerOverlapAfterCut(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 30, 'sponsor'), self._sponsor_chapter(20, 50, 'interaction'), self._sponsor_chapter(30, 50, 'selfpromo', remove=True), @@ -310,7 +329,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, expected, [self._chapter(30, 50, remove=True)]) def test_remove_marked_arrange_sponsors_SponsorsStillOverlapAfterCut(self): - chapters = self._chapters([70], ['c']) + [ + chapters = [ + *self._chapters([70], ['c']), self._sponsor_chapter(10, 60, 'sponsor'), self._sponsor_chapter(20, 60, 'interaction'), self._sponsor_chapter(30, 50, 'selfpromo', remove=True)] @@ -321,7 +341,8 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, expected, [self._chapter(30, 50, remove=True)]) def test_remove_marked_arrange_sponsors_ChapterWithRunsOfOverlappingSponsorsAndCuts(self): - chapters = self._chapters([200], ['c']) + [ + chapters = [ + *self._chapters([200], ['c']), self._sponsor_chapter(10, 40, 'sponsor'), self._sponsor_chapter(10, 30, 'intro'), self._chapter(20, 30, remove=True), @@ -347,8 +368,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, expected_cuts) def test_remove_marked_arrange_sponsors_SponsorOverlapsMultipleChapters(self): - chapters = (self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']) - + [self._sponsor_chapter(10, 90, 'sponsor')]) + chapters = [ + *self._chapters([20, 40, 60, 80, 100], ['c1', 'c2', 'c3', 'c4', 'c5']), + self._sponsor_chapter(10, 90, 'sponsor')] expected = self._chapters([10, 90, 100], ['c1', '[SponsorBlock]: Sponsor', 'c5']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -359,9 +381,10 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorsWithinSomeChaptersAndOverlappingOthers(self): - chapters = (self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']) - + [self._sponsor_chapter(20, 30, 'sponsor'), - self._sponsor_chapter(50, 70, 'selfpromo')]) + chapters = [ + *self._chapters([10, 40, 60, 80], ['c1', 'c2', 'c3', 'c4']), + self._sponsor_chapter(20, 30, 'sponsor'), + self._sponsor_chapter(50, 70, 'selfpromo')] expected = self._chapters([10, 20, 30, 40, 50, 70, 80], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c2', 'c3', '[SponsorBlock]: Unpaid/Self Promotion', 'c4']) @@ -374,8 +397,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_ChaptersAfterLastSponsor(self): - chapters = (self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']) - + [self._sponsor_chapter(10, 30, 'music_offtopic')]) + chapters = [ + *self._chapters([20, 40, 50, 60], ['c1', 'c2', 'c3', 'c4']), + self._sponsor_chapter(10, 30, 'music_offtopic')] expected = self._chapters( [10, 30, 40, 50, 60], ['c1', '[SponsorBlock]: Non-Music Section', 'c2', 'c3', 'c4']) @@ -388,8 +412,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorStartsAtChapterStart(self): - chapters = (self._chapters([10, 20, 40], ['c1', 'c2', 'c3']) - + [self._sponsor_chapter(20, 30, 'sponsor')]) + chapters = [ + *self._chapters([10, 20, 40], ['c1', 'c2', 'c3']), + self._sponsor_chapter(20, 30, 'sponsor')] expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -400,8 +425,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorEndsAtChapterEnd(self): - chapters = (self._chapters([10, 30, 40], ['c1', 'c2', 'c3']) - + [self._sponsor_chapter(20, 30, 'sponsor')]) + chapters = [ + *self._chapters([10, 30, 40], ['c1', 'c2', 'c3']), + self._sponsor_chapter(20, 30, 'sponsor')] expected = self._chapters([10, 20, 30, 40], ['c1', 'c2', '[SponsorBlock]: Sponsor', 'c3']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -412,8 +438,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorCoincidesWithChapters(self): - chapters = (self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) - + [self._sponsor_chapter(10, 30, 'sponsor')]) + chapters = [ + *self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']), + self._sponsor_chapter(10, 30, 'sponsor')] expected = self._chapters([10, 30, 40], ['c1', '[SponsorBlock]: Sponsor', 'c4']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -424,8 +451,9 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorsAtVideoBoundaries(self): - chapters = (self._chapters([20, 40, 60], ['c1', 'c2', 'c3']) - + [self._sponsor_chapter(0, 10, 'intro'), self._sponsor_chapter(50, 60, 'outro')]) + chapters = [ + *self._chapters([20, 40, 60], ['c1', 'c2', 'c3']), + self._sponsor_chapter(0, 10, 'intro'), self._sponsor_chapter(50, 60, 'outro')] expected = self._chapters( [10, 20, 40, 50, 60], ['[SponsorBlock]: Intermission/Intro Animation', 'c1', 'c2', 'c3', '[SponsorBlock]: Endcards/Credits']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -437,8 +465,10 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_SponsorsOverlapChaptersAtVideoBoundaries(self): - chapters = (self._chapters([10, 40, 50], ['c1', 'c2', 'c3']) - + [self._sponsor_chapter(0, 20, 'intro'), self._sponsor_chapter(30, 50, 'outro')]) + chapters = [ + *self._chapters([10, 40, 50], ['c1', 'c2', 'c3']), + self._sponsor_chapter(0, 20, 'intro'), + self._sponsor_chapter(30, 50, 'outro')] expected = self._chapters( [20, 30, 50], ['[SponsorBlock]: Intermission/Intro Animation', 'c2', '[SponsorBlock]: Endcards/Credits']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -450,8 +480,10 @@ class TestModifyChaptersPP(unittest.TestCase): self._remove_marked_arrange_sponsors_test_impl(chapters, expected, cuts) def test_remove_marked_arrange_sponsors_EverythingSponsored(self): - chapters = (self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']) - + [self._sponsor_chapter(0, 20, 'intro'), self._sponsor_chapter(20, 40, 'outro')]) + chapters = [ + *self._chapters([10, 20, 30, 40], ['c1', 'c2', 'c3', 'c4']), + self._sponsor_chapter(0, 20, 'intro'), + self._sponsor_chapter(20, 40, 'outro')] expected = self._chapters([20, 40], ['[SponsorBlock]: Intermission/Intro Animation', '[SponsorBlock]: Endcards/Credits']) self._remove_marked_arrange_sponsors_test_impl(chapters, expected, []) @@ -491,38 +523,39 @@ class TestModifyChaptersPP(unittest.TestCase): chapters, self._chapters([2.5], ['c2']), cuts) def test_remove_marked_arrange_sponsors_TinyChaptersResultingFromSponsorOverlapAreIgnored(self): - chapters = self._chapters([1, 3, 4], ['c1', 'c2', 'c3']) + [ + chapters = [ + *self._chapters([1, 3, 4], ['c1', 'c2', 'c3']), self._sponsor_chapter(1.5, 2.5, 'sponsor')] self._remove_marked_arrange_sponsors_test_impl( chapters, self._chapters([1.5, 2.5, 4], ['c1', '[SponsorBlock]: Sponsor', 'c3']), []) def test_remove_marked_arrange_sponsors_TinySponsorsOverlapsAreIgnored(self): - chapters = self._chapters([2, 3, 5], ['c1', 'c2', 'c3']) + [ + chapters = [ + *self._chapters([2, 3, 5], ['c1', 'c2', 'c3']), self._sponsor_chapter(1, 3, 'sponsor'), - self._sponsor_chapter(2.5, 4, 'selfpromo') - ] + self._sponsor_chapter(2.5, 4, 'selfpromo')] self._remove_marked_arrange_sponsors_test_impl( chapters, self._chapters([1, 3, 4, 5], [ 'c1', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', 'c3']), []) def test_remove_marked_arrange_sponsors_TinySponsorsPrependedToTheNextSponsor(self): - chapters = self._chapters([4], ['c']) + [ + chapters = [ + *self._chapters([4], ['c']), self._sponsor_chapter(1.5, 2, 'sponsor'), - self._sponsor_chapter(2, 4, 'selfpromo') - ] + self._sponsor_chapter(2, 4, 'selfpromo')] self._remove_marked_arrange_sponsors_test_impl( chapters, self._chapters([1.5, 4], ['c', '[SponsorBlock]: Unpaid/Self Promotion']), []) def test_remove_marked_arrange_sponsors_SmallestSponsorInTheOverlapGetsNamed(self): self._pp._sponsorblock_chapter_title = '[SponsorBlock]: %(name)s' - chapters = self._chapters([10], ['c']) + [ + chapters = [ + *self._chapters([10], ['c']), self._sponsor_chapter(2, 8, 'sponsor'), - self._sponsor_chapter(4, 6, 'selfpromo') - ] + self._sponsor_chapter(4, 6, 'selfpromo')] self._remove_marked_arrange_sponsors_test_impl( chapters, self._chapters([2, 4, 6, 8, 10], [ 'c', '[SponsorBlock]: Sponsor', '[SponsorBlock]: Unpaid/Self Promotion', - '[SponsorBlock]: Sponsor', 'c' + '[SponsorBlock]: Sponsor', 'c', ]), []) def test_make_concat_opts_CommonCase(self): diff --git a/test/test_socks.py b/test/test_socks.py index 43d612d..68af19d 100644 --- a/test/test_socks.py +++ b/test/test_socks.py @@ -95,7 +95,7 @@ class Socks5ProxyHandler(StreamRequestHandler, SocksProxyHandler): return elif Socks5Auth.AUTH_USER_PASS in methods: - self.connection.sendall(struct.pack("!BB", SOCKS5_VERSION, Socks5Auth.AUTH_USER_PASS)) + self.connection.sendall(struct.pack('!BB', SOCKS5_VERSION, Socks5Auth.AUTH_USER_PASS)) _, user_len = struct.unpack('!BB', self.connection.recv(2)) username = self.connection.recv(user_len).decode() @@ -174,7 +174,7 @@ class Socks4ProxyHandler(StreamRequestHandler, SocksProxyHandler): if 0x0 < dest_ip <= 0xFF: use_remote_dns = True else: - socks_info['ipv4_address'] = socket.inet_ntoa(struct.pack("!I", dest_ip)) + socks_info['ipv4_address'] = socket.inet_ntoa(struct.pack('!I', dest_ip)) user_id = self._read_until_null().decode() if user_id != (self.socks_kwargs.get('user_id') or ''): @@ -291,7 +291,7 @@ def ctx(request): ('Urllib', 'http'), ('Requests', 'http'), ('Websockets', 'ws'), - ('CurlCFFI', 'http') + ('CurlCFFI', 'http'), ], indirect=True) class TestSocks4Proxy: def test_socks4_no_auth(self, handler, ctx): @@ -366,7 +366,7 @@ class TestSocks4Proxy: ('Urllib', 'http'), ('Requests', 'http'), ('Websockets', 'ws'), - ('CurlCFFI', 'http') + ('CurlCFFI', 'http'), ], indirect=True) class TestSocks5Proxy: diff --git a/test/test_subtitles.py b/test/test_subtitles.py index 5736289..f3b0056 100644 --- a/test/test_subtitles.py +++ b/test/test_subtitles.py @@ -40,12 +40,11 @@ class BaseTestSubtitles(unittest.TestCase): self.ie = self.IE() self.DL.add_info_extractor(self.ie) if not self.IE.working(): - print('Skipping: %s marked as not _WORKING' % self.IE.ie_key()) + print(f'Skipping: {self.IE.ie_key()} marked as not _WORKING') self.skipTest('IE marked as not _WORKING') def getInfoDict(self): - info_dict = self.DL.extract_info(self.url, download=False) - return info_dict + return self.DL.extract_info(self.url, download=False) def getSubtitles(self): info_dict = self.getInfoDict() @@ -87,7 +86,7 @@ class TestYoutubeSubtitles(BaseTestSubtitles): self.assertEqual(md5(subtitles['en']), 'ae1bd34126571a77aabd4d276b28044d') self.assertEqual(md5(subtitles['it']), '0e0b667ba68411d88fd1c5f4f4eab2f9') for lang in ['fr', 'de']: - self.assertTrue(subtitles.get(lang) is not None, 'Subtitles for \'%s\' not extracted' % lang) + self.assertTrue(subtitles.get(lang) is not None, f'Subtitles for \'{lang}\' not extracted') def _test_subtitles_format(self, fmt, md5_hash, lang='en'): self.DL.params['writesubtitles'] = True @@ -157,7 +156,7 @@ class TestDailymotionSubtitles(BaseTestSubtitles): self.assertEqual(md5(subtitles['en']), '976553874490cba125086bbfea3ff76f') self.assertEqual(md5(subtitles['fr']), '594564ec7d588942e384e920e5341792') for lang in ['es', 'fr', 'de']: - self.assertTrue(subtitles.get(lang) is not None, 'Subtitles for \'%s\' not extracted' % lang) + self.assertTrue(subtitles.get(lang) is not None, f'Subtitles for \'{lang}\' not extracted') def test_nosubtitles(self): self.DL.expect_warning('video doesn\'t have subtitles') @@ -182,7 +181,7 @@ class TestTedSubtitles(BaseTestSubtitles): self.assertEqual(md5(subtitles['en']), '4262c1665ff928a2dada178f62cb8d14') self.assertEqual(md5(subtitles['fr']), '66a63f7f42c97a50f8c0e90bc7797bb5') for lang in ['es', 'fr', 'de']: - self.assertTrue(subtitles.get(lang) is not None, 'Subtitles for \'%s\' not extracted' % lang) + self.assertTrue(subtitles.get(lang) is not None, f'Subtitles for \'{lang}\' not extracted') @is_download_test diff --git a/test/test_traversal.py b/test/test_traversal.py index 9b2a27b..5d9fbe1 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -31,7 +31,7 @@ class TestTraversal: 'allow tuple path' assert traverse_obj(_TEST_DATA, ['str']) == 'str', \ 'allow list path' - assert traverse_obj(_TEST_DATA, (value for value in ("str",))) == 'str', \ + assert traverse_obj(_TEST_DATA, (value for value in ('str',))) == 'str', \ 'allow iterable path' assert traverse_obj(_TEST_DATA, 'str') == 'str', \ 'single items should be treated as a path' @@ -70,7 +70,7 @@ class TestTraversal: def test_traversal_set(self): # transformation/type, like `expected_type` - assert traverse_obj(_TEST_DATA, (..., {str.upper}, )) == ['STR'], \ + assert traverse_obj(_TEST_DATA, (..., {str.upper})) == ['STR'], \ 'Function in set should be a transformation' assert traverse_obj(_TEST_DATA, (..., {str})) == ['str'], \ 'Type in set should be a type filter' @@ -276,7 +276,7 @@ class TestTraversal: '`...` should result in string (same value) if `traverse_string`' assert traverse_obj(_TRAVERSE_STRING_DATA, ('str', slice(0, None, 2)), traverse_string=True) == 'sr', \ '`slice` should result in string if `traverse_string`' - assert traverse_obj(_TRAVERSE_STRING_DATA, ('str', lambda i, v: i or v == "s"), traverse_string=True) == 'str', \ + assert traverse_obj(_TRAVERSE_STRING_DATA, ('str', lambda i, v: i or v == 's'), traverse_string=True) == 'str', \ 'function should result in string if `traverse_string`' assert traverse_obj(_TRAVERSE_STRING_DATA, ('str', (0, 2)), traverse_string=True) == ['s', 'r'], \ 'branching should result in list if `traverse_string`' diff --git a/test/test_update.py b/test/test_update.py index bc13956..63a21e4 100644 --- a/test/test_update.py +++ b/test/test_update.py @@ -78,11 +78,11 @@ TEST_API_DATA = { TEST_LOCKFILE_COMMENT = '# This file is used for regulating self-update' -TEST_LOCKFILE_V1 = r'''%s +TEST_LOCKFILE_V1 = rf'''{TEST_LOCKFILE_COMMENT} lock 2022.08.18.36 .+ Python 3\.6 lock 2023.11.16 (?!win_x86_exe).+ Python 3\.7 lock 2023.11.16 win_x86_exe .+ Windows-(?:Vista|2008Server) -''' % TEST_LOCKFILE_COMMENT +''' TEST_LOCKFILE_V2_TMPL = r'''%s lockV2 yt-dlp/yt-dlp 2022.08.18.36 .+ Python 3\.6 @@ -98,12 +98,12 @@ TEST_LOCKFILE_V2 = TEST_LOCKFILE_V2_TMPL % TEST_LOCKFILE_COMMENT TEST_LOCKFILE_ACTUAL = TEST_LOCKFILE_V2_TMPL % TEST_LOCKFILE_V1.rstrip('\n') -TEST_LOCKFILE_FORK = r'''%s# Test if a fork blocks updates to non-numeric tags +TEST_LOCKFILE_FORK = rf'''{TEST_LOCKFILE_ACTUAL}# Test if a fork blocks updates to non-numeric tags lockV2 fork/yt-dlp pr0000 .+ Python 3.6 lockV2 fork/yt-dlp pr1234 (?!win_x86_exe).+ Python 3\.7 lockV2 fork/yt-dlp pr1234 win_x86_exe .+ Windows-(?:Vista|2008Server) lockV2 fork/yt-dlp pr9999 .+ Python 3.11 -''' % TEST_LOCKFILE_ACTUAL +''' class FakeUpdater(Updater): diff --git a/test/test_utils.py b/test/test_utils.py index 77fadbb..3ff1f8b 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -130,6 +130,7 @@ from yt_dlp.utils import ( xpath_text, xpath_with_ns, ) +from yt_dlp.utils._utils import _UnsafeExtensionError from yt_dlp.utils.networking import ( HTTPHeaderDict, escape_rfc3986, @@ -276,11 +277,18 @@ class TestUtil(unittest.TestCase): self.assertEqual(expand_path(env('HOME')), os.getenv('HOME')) self.assertEqual(expand_path('~'), os.getenv('HOME')) self.assertEqual( - expand_path('~/%s' % env('yt_dlp_EXPATH_PATH')), - '%s/expanded' % os.getenv('HOME')) + expand_path('~/{}'.format(env('yt_dlp_EXPATH_PATH'))), + '{}/expanded'.format(os.getenv('HOME'))) finally: os.environ['HOME'] = old_home or '' + _uncommon_extensions = [ + ('exe', 'abc.exe.ext'), + ('de', 'abc.de.ext'), + ('../.mp4', None), + ('..\\.mp4', None), + ] + def test_prepend_extension(self): self.assertEqual(prepend_extension('abc.ext', 'temp'), 'abc.temp.ext') self.assertEqual(prepend_extension('abc.ext', 'temp', 'ext'), 'abc.temp.ext') @@ -289,6 +297,19 @@ class TestUtil(unittest.TestCase): self.assertEqual(prepend_extension('.abc', 'temp'), '.abc.temp') self.assertEqual(prepend_extension('.abc.ext', 'temp'), '.abc.temp.ext') + # Test uncommon extensions + self.assertEqual(prepend_extension('abc.ext', 'bin'), 'abc.bin.ext') + for ext, result in self._uncommon_extensions: + with self.assertRaises(_UnsafeExtensionError): + prepend_extension('abc', ext) + if result: + self.assertEqual(prepend_extension('abc.ext', ext, 'ext'), result) + else: + with self.assertRaises(_UnsafeExtensionError): + prepend_extension('abc.ext', ext, 'ext') + with self.assertRaises(_UnsafeExtensionError): + prepend_extension('abc.unexpected_ext', ext, 'ext') + def test_replace_extension(self): self.assertEqual(replace_extension('abc.ext', 'temp'), 'abc.temp') self.assertEqual(replace_extension('abc.ext', 'temp', 'ext'), 'abc.temp') @@ -297,6 +318,16 @@ class TestUtil(unittest.TestCase): self.assertEqual(replace_extension('.abc', 'temp'), '.abc.temp') self.assertEqual(replace_extension('.abc.ext', 'temp'), '.abc.temp') + # Test uncommon extensions + self.assertEqual(replace_extension('abc.ext', 'bin'), 'abc.unknown_video') + for ext, _ in self._uncommon_extensions: + with self.assertRaises(_UnsafeExtensionError): + replace_extension('abc', ext) + with self.assertRaises(_UnsafeExtensionError): + replace_extension('abc.ext', ext, 'ext') + with self.assertRaises(_UnsafeExtensionError): + replace_extension('abc.unexpected_ext', ext, 'ext') + def test_subtitles_filename(self): self.assertEqual(subtitles_filename('abc.ext', 'en', 'vtt'), 'abc.en.vtt') self.assertEqual(subtitles_filename('abc.ext', 'en', 'vtt', 'ext'), 'abc.en.vtt') @@ -356,12 +387,12 @@ class TestUtil(unittest.TestCase): self.assertEqual(datetime_from_str('now+23hours', precision='hour'), datetime_from_str('now+23hours', precision='auto')) def test_daterange(self): - _20century = DateRange("19000101", "20000101") - self.assertFalse("17890714" in _20century) - _ac = DateRange("00010101") - self.assertTrue("19690721" in _ac) - _firstmilenium = DateRange(end="10000101") - self.assertTrue("07110427" in _firstmilenium) + _20century = DateRange('19000101', '20000101') + self.assertFalse('17890714' in _20century) + _ac = DateRange('00010101') + self.assertTrue('19690721' in _ac) + _firstmilenium = DateRange(end='10000101') + self.assertTrue('07110427' in _firstmilenium) def test_unified_dates(self): self.assertEqual(unified_strdate('December 21, 2010'), '20101221') @@ -506,7 +537,7 @@ class TestUtil(unittest.TestCase): self.assertRaises(ExtractorError, xpath_attr, doc, 'div/p', 'y', fatal=True) def test_smuggle_url(self): - data = {"ö": "ö", "abc": [3]} + data = {'ö': 'ö', 'abc': [3]} url = 'https://foo.bar/baz?x=y#a' smug_url = smuggle_url(url, data) unsmug_url, unsmug_data = unsmuggle_url(smug_url) @@ -784,7 +815,7 @@ class TestUtil(unittest.TestCase): def test_strip_jsonp(self): stripped = strip_jsonp('cb ([ {"id":"532cb",\n\n\n"x":\n3}\n]\n);') d = json.loads(stripped) - self.assertEqual(d, [{"id": "532cb", "x": 3}]) + self.assertEqual(d, [{'id': '532cb', 'x': 3}]) stripped = strip_jsonp('parseMetadata({"STATUS":"OK"})\n\n\n//epc') d = json.loads(stripped) @@ -922,19 +953,19 @@ class TestUtil(unittest.TestCase): def test_normalize_url(self): self.assertEqual( normalize_url('http://wowza.imust.org/srv/vod/telemb/new/UPLOAD/UPLOAD/20224_IncendieHavré_FD.mp4'), - 'http://wowza.imust.org/srv/vod/telemb/new/UPLOAD/UPLOAD/20224_IncendieHavre%CC%81_FD.mp4' + 'http://wowza.imust.org/srv/vod/telemb/new/UPLOAD/UPLOAD/20224_IncendieHavre%CC%81_FD.mp4', ) self.assertEqual( normalize_url('http://www.ardmediathek.de/tv/Sturm-der-Liebe/Folge-2036-Zu-Mann-und-Frau-erklärt/Das-Erste/Video?documentId=22673108&bcastId=5290'), - 'http://www.ardmediathek.de/tv/Sturm-der-Liebe/Folge-2036-Zu-Mann-und-Frau-erkl%C3%A4rt/Das-Erste/Video?documentId=22673108&bcastId=5290' + 'http://www.ardmediathek.de/tv/Sturm-der-Liebe/Folge-2036-Zu-Mann-und-Frau-erkl%C3%A4rt/Das-Erste/Video?documentId=22673108&bcastId=5290', ) self.assertEqual( normalize_url('http://тест.рф/фрагмент'), - 'http://xn--e1aybc.xn--p1ai/%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82' + 'http://xn--e1aybc.xn--p1ai/%D1%84%D1%80%D0%B0%D0%B3%D0%BC%D0%B5%D0%BD%D1%82', ) self.assertEqual( normalize_url('http://тест.рф/абв?абв=абв#абв'), - 'http://xn--e1aybc.xn--p1ai/%D0%B0%D0%B1%D0%B2?%D0%B0%D0%B1%D0%B2=%D0%B0%D0%B1%D0%B2#%D0%B0%D0%B1%D0%B2' + 'http://xn--e1aybc.xn--p1ai/%D0%B0%D0%B1%D0%B2?%D0%B0%D0%B1%D0%B2=%D0%B0%D0%B1%D0%B2#%D0%B0%D0%B1%D0%B2', ) self.assertEqual(normalize_url('http://vimeo.com/56015672#at=0'), 'http://vimeo.com/56015672#at=0') @@ -979,7 +1010,7 @@ class TestUtil(unittest.TestCase): 'e': 'false', 'f': '"false"', 'g': 'var', - } + }, )), { 'null': None, @@ -988,8 +1019,8 @@ class TestUtil(unittest.TestCase): 'trueStr': 'true', 'false': False, 'falseStr': 'false', - 'unresolvedVar': 'var' - } + 'unresolvedVar': 'var', + }, ) self.assertDictEqual( @@ -1005,14 +1036,14 @@ class TestUtil(unittest.TestCase): 'b': '"123"', 'c': '1.23', 'd': '"1.23"', - } + }, )), { 'int': 123, 'intStr': '123', 'float': 1.23, 'floatStr': '1.23', - } + }, ) self.assertDictEqual( @@ -1028,14 +1059,14 @@ class TestUtil(unittest.TestCase): 'b': '"{}"', 'c': '[]', 'd': '"[]"', - } + }, )), { 'object': {}, 'objectStr': '{}', 'array': [], 'arrayStr': '[]', - } + }, ) def test_js_to_json_realworld(self): @@ -1081,7 +1112,7 @@ class TestUtil(unittest.TestCase): def test_js_to_json_edgecases(self): on = js_to_json("{abc_def:'1\\'\\\\2\\\\\\'3\"4'}") - self.assertEqual(json.loads(on), {"abc_def": "1'\\2\\'3\"4"}) + self.assertEqual(json.loads(on), {'abc_def': "1'\\2\\'3\"4"}) on = js_to_json('{"abc": true}') self.assertEqual(json.loads(on), {'abc': True}) @@ -1113,9 +1144,9 @@ class TestUtil(unittest.TestCase): 'c': 0, 'd': 42.42, 'e': [], - 'f': "abc", - 'g': "", - '42': 42 + 'f': 'abc', + 'g': '', + '42': 42, }) on = js_to_json('["abc", "def",]') @@ -1209,8 +1240,8 @@ class TestUtil(unittest.TestCase): self.assertEqual(json.loads(js_to_json('Array(5, 10)')), [5, 10]) self.assertEqual(json.loads(js_to_json('new Array(15,5)')), [15, 5]) self.assertEqual(json.loads(js_to_json('new Map([Array(5, 10),new Array(15,5)])')), {'5': 10, '15': 5}) - self.assertEqual(json.loads(js_to_json('new Date("123")')), "123") - self.assertEqual(json.loads(js_to_json('new Date(\'2023-10-19\')')), "2023-10-19") + self.assertEqual(json.loads(js_to_json('new Date("123")')), '123') + self.assertEqual(json.loads(js_to_json('new Date(\'2023-10-19\')')), '2023-10-19') def test_extract_attributes(self): self.assertEqual(extract_attributes('<e x="y">'), {'x': 'y'}) @@ -1265,7 +1296,7 @@ class TestUtil(unittest.TestCase): def test_args_to_str(self): self.assertEqual( args_to_str(['foo', 'ba/r', '-baz', '2 be', '']), - 'foo ba/r -baz \'2 be\' \'\'' if compat_os_name != 'nt' else 'foo ba/r -baz "2 be" ""' + 'foo ba/r -baz \'2 be\' \'\'' if compat_os_name != 'nt' else 'foo ba/r -baz "2 be" ""', ) def test_parse_filesize(self): @@ -1348,10 +1379,10 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4') self.assertTrue(is_html( # UTF-8 with BOM b'\xef\xbb\xbf<!DOCTYPE foo>\xaaa')) self.assertTrue(is_html( # UTF-16-LE - b'\xff\xfe<\x00h\x00t\x00m\x00l\x00>\x00\xe4\x00' + b'\xff\xfe<\x00h\x00t\x00m\x00l\x00>\x00\xe4\x00', )) self.assertTrue(is_html( # UTF-16-BE - b'\xfe\xff\x00<\x00h\x00t\x00m\x00l\x00>\x00\xe4' + b'\xfe\xff\x00<\x00h\x00t\x00m\x00l\x00>\x00\xe4', )) self.assertTrue(is_html( # UTF-32-BE b'\x00\x00\xFE\xFF\x00\x00\x00<\x00\x00\x00h\x00\x00\x00t\x00\x00\x00m\x00\x00\x00l\x00\x00\x00>\x00\x00\x00\xe4')) @@ -1935,7 +1966,7 @@ Line 1 with locked_file(FILE, test_mode, False): pass except (BlockingIOError, PermissionError): - if not testing_write: # FIXME + if not testing_write: # FIXME: blocked read access print(f'Known issue: Exclusive lock ({lock_mode}) blocks read access ({test_mode})') continue self.assertTrue(testing_write, f'{test_mode} is blocked by {lock_mode}') @@ -2003,7 +2034,7 @@ Line 1 msg='int fn with expected_type int should give int') self.assertEqual(try_call(lambda: 1, expected_type=dict), None, msg='int fn with wrong expected_type should give None') - self.assertEqual(try_call(total, args=(0, 1, 0, ), expected_type=int), 1, + self.assertEqual(try_call(total, args=(0, 1, 0), expected_type=int), 1, msg='fn should accept arglist') self.assertEqual(try_call(total, kwargs={'a': 0, 'b': 1, 'c': 0}, expected_type=int), 1, msg='fn should accept kwargs') diff --git a/test/test_websockets.py b/test/test_websockets.py index aa0dfa2..5f101ab 100644 --- a/test/test_websockets.py +++ b/test/test_websockets.py @@ -297,14 +297,14 @@ class TestWebsSocketRequestHandlerConformance: 'client_certificate': os.path.join(MTLS_CERT_DIR, 'client.crt'), 'client_certificate_key': os.path.join(MTLS_CERT_DIR, 'clientencrypted.key'), 'client_certificate_password': 'foobar', - } + }, )) def test_mtls(self, handler, client_cert): with handler( # Disable client-side validation of unacceptable self-signed testcert.pem # The test is of a check on the server side, so unaffected verify=False, - client_cert=client_cert + client_cert=client_cert, ) as rh: ws_validate_and_send(rh, Request(self.mtls_wss_base_url)).close() diff --git a/test/test_youtube_misc.py b/test/test_youtube_misc.py index 81be5d3..81b1162 100644 --- a/test/test_youtube_misc.py +++ b/test/test_youtube_misc.py @@ -13,7 +13,7 @@ from yt_dlp.extractor import YoutubeIE class TestYoutubeMisc(unittest.TestCase): def test_youtube_extract(self): - assertExtractId = lambda url, id: self.assertEqual(YoutubeIE.extract_id(url), id) + assertExtractId = lambda url, video_id: self.assertEqual(YoutubeIE.extract_id(url), video_id) assertExtractId('http://www.youtube.com/watch?&v=BaW_jenozKc', 'BaW_jenozKc') assertExtractId('https://www.youtube.com/watch?&v=BaW_jenozKc', 'BaW_jenozKc') assertExtractId('https://www.youtube.com/watch?feature=player_embedded&v=BaW_jenozKc', 'BaW_jenozKc') diff --git a/test/test_youtube_signature.py b/test/test_youtube_signature.py index c559284..b0f3269 100644 --- a/test/test_youtube_signature.py +++ b/test/test_youtube_signature.py @@ -46,17 +46,17 @@ _SIG_TESTS = [ ( 'https://s.ytimg.com/yts/jsbin/html5player-en_US-vflBb0OQx.js', 84, - '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ0STUVWXYZ!"#$%&\'()*+,@./:;<=>' + '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQ0STUVWXYZ!"#$%&\'()*+,@./:;<=>', ), ( 'https://s.ytimg.com/yts/jsbin/html5player-en_US-vfl9FYC6l.js', 83, - '123456789abcdefghijklmnopqr0tuvwxyzABCDETGHIJKLMNOPQRS>UVWXYZ!"#$%&\'()*+,-./:;<=F' + '123456789abcdefghijklmnopqr0tuvwxyzABCDETGHIJKLMNOPQRS>UVWXYZ!"#$%&\'()*+,-./:;<=F', ), ( 'https://s.ytimg.com/yts/jsbin/html5player-en_US-vflCGk6yw/html5player.js', '4646B5181C6C3020DF1D9C7FCFEA.AD80ABF70C39BD369CCCAE780AFBB98FA6B6CB42766249D9488C288', - '82C8849D94266724DC6B6AF89BBFA087EACCD963.B93C07FBA084ACAEFCF7C9D1FD0203C6C1815B6B' + '82C8849D94266724DC6B6AF89BBFA087EACCD963.B93C07FBA084ACAEFCF7C9D1FD0203C6C1815B6B', ), ( 'https://s.ytimg.com/yts/jsbin/html5player-en_US-vflKjOTVq/html5player.js', @@ -163,6 +163,10 @@ _NSIG_TESTS = [ 'https://www.youtube.com/s/player/b7910ca8/player_ias.vflset/en_US/base.js', '_hXMCwMt9qE310D', 'LoZMgkkofRMCZQ', ), + ( + 'https://www.youtube.com/s/player/590f65a6/player_ias.vflset/en_US/base.js', + '1tm7-g_A9zsI8_Lay_', 'xI4Vem4Put_rOg', + ), ] @@ -207,7 +211,7 @@ class TestSignature(unittest.TestCase): def t_factory(name, sig_func, url_pattern): def make_tfunc(url, sig_input, expected_sig): m = url_pattern.match(url) - assert m, '%r should follow URL format' % url + assert m, f'{url!r} should follow URL format' test_id = m.group('id') def test_func(self): |