]> jfr.im git - yt-dlp.git/blame - yt_dlp/compat.py
[MainStreaming] Add extractor (#2180)
[yt-dlp.git] / yt_dlp / compat.py
CommitLineData
dfe5fa49 1# coding: utf-8
451948b2 2
c634ad2a 3import asyncio
f206126d 4import base64
d7cd9a9e 5import ctypes
8c25f81b 6import getpass
c634ad2a 7import html
8import html.parser
9import http
10import http.client
11import http.cookiejar
12import http.cookies
13import http.server
2384f5a6 14import itertools
e07e9313 15import optparse
8c25f81b 16import os
7d4111ed 17import re
51f579b6 18import shlex
003c69a8 19import shutil
be4a824d 20import socket
dab0daee 21import struct
673944b0 22import subprocess
8c25f81b 23import sys
c634ad2a 24import tokenize
25import urllib
26import xml.etree.ElementTree as etree
27from subprocess import DEVNULL
72b40955 28
72b40955 29
c634ad2a 30# HTMLParseError has been deprecated in Python 3.3 and removed in
31# Python 3.5. Introducing dummy exception for Python >3.5 for compatible
32# and uniform cross-version exception handling
33class compat_HTMLParseError(Exception):
34 pass
15707c7e 35
15707c7e 36
e6f21b3d 37# compat_ctypes_WINFUNCTYPE = ctypes.WINFUNCTYPE
38# will not work since ctypes.WINFUNCTYPE does not exist in UNIX machines
c634ad2a 39def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
40 return ctypes.WINFUNCTYPE(*args, **kwargs)
eb7941e3
YCH
41
42
43class _TreeBuilder(etree.TreeBuilder):
44 def doctype(self, name, pubid, system):
45 pass
46
582be358 47
c634ad2a 48def compat_etree_fromstring(text):
49 return etree.XML(text, parser=etree.XMLParser(target=_TreeBuilder()))
8c25f81b 50
b08e235f
S
51
52compat_os_name = os._name if os.name == 'java' else os.name
53
54
55if compat_os_name == 'nt':
702ccf2d 56 def compat_shlex_quote(s):
b08e235f
S
57 return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
58else:
c634ad2a 59 from shlex import quote as compat_shlex_quote
51f579b6
S
60
61
8c25f81b 62def compat_ord(c):
5f6a1245
JW
63 if type(c) is int:
64 return c
65 else:
66 return ord(c)
8c25f81b
PH
67
68
c634ad2a 69def compat_setenv(key, value, env=os.environ):
70 env[key] = value
8c25f81b
PH
71
72
82fea5b4
S
73if compat_os_name == 'nt' and sys.version_info < (3, 8):
74 # os.path.realpath on Windows does not follow symbolic links
75 # prior to Python 3.8 (see https://bugs.python.org/issue9949)
76 def compat_realpath(path):
77 while os.path.islink(path):
78 path = os.path.abspath(os.readlink(path))
79 return path
80else:
81 compat_realpath = os.path.realpath
82
83
c634ad2a 84def compat_print(s):
85 assert isinstance(s, compat_str)
86 print(s)
be4a824d
PH
87
88
067aa17e 89# Fix https://github.com/ytdl-org/youtube-dl/issues/4223
e07e9313
PH
90# See http://bugs.python.org/issue9161 for what is broken
91def workaround_optparse_bug9161():
07e378fa
PH
92 op = optparse.OptionParser()
93 og = optparse.OptionGroup(op, 'foo')
e07e9313 94 try:
07e378fa 95 og.add_option('-t')
b244b5c3 96 except TypeError:
e07e9313
PH
97 real_add_option = optparse.OptionGroup.add_option
98
99 def _compat_add_option(self, *args, **kwargs):
100 enc = lambda v: (
101 v.encode('ascii', 'replace') if isinstance(v, compat_str)
102 else v)
103 bargs = [enc(a) for a in args]
104 bkwargs = dict(
105 (k, enc(v)) for k, v in kwargs.items())
106 return real_add_option(self, *bargs, **bkwargs)
107 optparse.OptionGroup.add_option = _compat_add_option
108
582be358 109
4a2f19ab
F
110try:
111 compat_Pattern = re.Pattern
112except AttributeError:
113 compat_Pattern = type(re.compile(''))
114
115
116try:
117 compat_Match = re.Match
118except AttributeError:
119 compat_Match = type(re.compile('').match(''))
120
121
e36d50c5 122try:
c634ad2a 123 compat_asyncio_run = asyncio.run # >= 3.7
e36d50c5 124except AttributeError:
125 def compat_asyncio_run(coro):
126 try:
127 loop = asyncio.get_event_loop()
128 except RuntimeError:
129 loop = asyncio.new_event_loop()
130 asyncio.set_event_loop(loop)
131 loop.run_until_complete(coro)
132
133 asyncio.run = compat_asyncio_run
134
135
c589c1d3 136# Python 3.8+ does not honor %HOME% on windows, but this breaks compatibility with youtube-dl
137# See https://github.com/yt-dlp/yt-dlp/issues/792
138# https://docs.python.org/3/library/os.path.html#os.path.expanduser
139if compat_os_name in ('nt', 'ce') and 'HOME' in os.environ:
140 _userhome = os.environ['HOME']
141
142 def compat_expanduser(path):
143 if not path.startswith('~'):
144 return path
145 i = path.replace('\\', '/', 1).find('/') # ~user
146 if i < 0:
147 i = len(path)
148 userhome = os.path.join(os.path.dirname(_userhome), path[1:i]) if i > 1 else _userhome
149 return userhome + path[i:]
150else:
151 compat_expanduser = os.path.expanduser
152
153
edf65256 154try:
155 from Cryptodome.Cipher import AES as compat_pycrypto_AES
156except ImportError:
157 try:
158 from Crypto.Cipher import AES as compat_pycrypto_AES
159 except ImportError:
160 compat_pycrypto_AES = None
161
162
e3c7d495 163WINDOWS_VT_MODE = False if compat_os_name == 'nt' else None
164
165
819e0531 166def windows_enable_vt_mode(): # TODO: Do this the proper way https://bugs.python.org/issue30075
167 if compat_os_name != 'nt':
168 return
e3c7d495 169 global WINDOWS_VT_MODE
673944b0 170 startupinfo = subprocess.STARTUPINFO()
171 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
e3c7d495 172 try:
173 subprocess.Popen('', shell=True, startupinfo=startupinfo)
174 WINDOWS_VT_MODE = True
175 except Exception:
176 pass
819e0531 177
178
c634ad2a 179# Deprecated
180
181compat_basestring = str
182compat_chr = chr
183compat_input = input
184compat_integer_types = (int, )
185compat_kwargs = lambda kwargs: kwargs
186compat_numeric_types = (int, float, complex)
187compat_str = str
188compat_xpath = lambda xpath: xpath
189compat_zip = zip
190
191compat_HTMLParser = html.parser.HTMLParser
192compat_HTTPError = urllib.error.HTTPError
193compat_Struct = struct.Struct
194compat_b64decode = base64.b64decode
195compat_cookiejar = http.cookiejar
196compat_cookiejar_Cookie = compat_cookiejar.Cookie
197compat_cookies = http.cookies
198compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
199compat_etree_Element = etree.Element
200compat_etree_register_namespace = etree.register_namespace
c634ad2a 201compat_get_terminal_size = shutil.get_terminal_size
202compat_getenv = os.getenv
203compat_getpass = getpass.getpass
204compat_html_entities = html.entities
205compat_html_entities_html5 = compat_html_entities.html5
206compat_http_client = http.client
207compat_http_server = http.server
208compat_itertools_count = itertools.count
209compat_parse_qs = urllib.parse.parse_qs
210compat_shlex_split = shlex.split
211compat_socket_create_connection = socket.create_connection
212compat_struct_pack = struct.pack
213compat_struct_unpack = struct.unpack
214compat_subprocess_get_DEVNULL = lambda: DEVNULL
215compat_tokenize_tokenize = tokenize.tokenize
216compat_urllib_error = urllib.error
217compat_urllib_parse = urllib.parse
218compat_urllib_parse_quote = urllib.parse.quote
219compat_urllib_parse_quote_plus = urllib.parse.quote_plus
220compat_urllib_parse_unquote = urllib.parse.unquote
221compat_urllib_parse_unquote_plus = urllib.parse.unquote_plus
222compat_urllib_parse_unquote_to_bytes = urllib.parse.unquote_to_bytes
223compat_urllib_parse_urlencode = urllib.parse.urlencode
224compat_urllib_parse_urlparse = urllib.parse.urlparse
225compat_urllib_parse_urlunparse = urllib.parse.urlunparse
226compat_urllib_request = urllib.request
227compat_urllib_request_DataHandler = urllib.request.DataHandler
228compat_urllib_response = urllib.response
229compat_urlparse = urllib.parse
230compat_urlretrieve = urllib.request.urlretrieve
231compat_xml_parse_error = etree.ParseError
232
233
234# Set public objects
235
8c25f81b 236__all__ = [
e3c7d495 237 'WINDOWS_VT_MODE',
b081f53b 238 'compat_HTMLParseError',
8bb56eee 239 'compat_HTMLParser',
8c25f81b 240 'compat_HTTPError',
4a2f19ab
F
241 'compat_Match',
242 'compat_Pattern',
65220c3b 243 'compat_Struct',
e36d50c5 244 'compat_asyncio_run',
f206126d 245 'compat_b64decode',
0196149c 246 'compat_basestring',
8c25f81b
PH
247 'compat_chr',
248 'compat_cookiejar',
6d874fee 249 'compat_cookiejar_Cookie',
799207e8 250 'compat_cookies',
f7ad7160 251 'compat_cookies_SimpleCookie',
d7cd9a9e 252 'compat_ctypes_WINFUNCTYPE',
399f7687 253 'compat_etree_Element',
36e6f62c 254 'compat_etree_fromstring',
da162c11 255 'compat_etree_register_namespace',
8c25f81b 256 'compat_expanduser',
003c69a8 257 'compat_get_terminal_size',
8c25f81b
PH
258 'compat_getenv',
259 'compat_getpass',
260 'compat_html_entities',
9631a94f 261 'compat_html_entities_html5',
8c25f81b 262 'compat_http_client',
83fda3c0 263 'compat_http_server',
e67f6880 264 'compat_input',
075a13d3 265 'compat_integer_types',
a0e060ac 266 'compat_itertools_count',
c7b0add8 267 'compat_kwargs',
28572a1a 268 'compat_numeric_types',
8c25f81b 269 'compat_ord',
e9c0cdd3 270 'compat_os_name',
8c25f81b
PH
271 'compat_parse_qs',
272 'compat_print',
edf65256 273 'compat_pycrypto_AES',
bfe2b8cf 274 'compat_realpath',
fe40f9ee 275 'compat_setenv',
702ccf2d 276 'compat_shlex_quote',
51f579b6 277 'compat_shlex_split',
be4a824d 278 'compat_socket_create_connection',
987493ae 279 'compat_str',
edaa23f8
YCH
280 'compat_struct_pack',
281 'compat_struct_unpack',
8c25f81b 282 'compat_subprocess_get_DEVNULL',
67134eab 283 'compat_tokenize_tokenize',
8c25f81b
PH
284 'compat_urllib_error',
285 'compat_urllib_parse',
732044af 286 'compat_urllib_parse_quote',
287 'compat_urllib_parse_quote_plus',
8c25f81b 288 'compat_urllib_parse_unquote',
aa99aa4e 289 'compat_urllib_parse_unquote_plus',
9fefc886 290 'compat_urllib_parse_unquote_to_bytes',
15707c7e 291 'compat_urllib_parse_urlencode',
8c25f81b 292 'compat_urllib_parse_urlparse',
732044af 293 'compat_urllib_parse_urlunparse',
8c25f81b 294 'compat_urllib_request',
0a67a363
YCH
295 'compat_urllib_request_DataHandler',
296 'compat_urllib_response',
8c25f81b
PH
297 'compat_urlparse',
298 'compat_urlretrieve',
299 'compat_xml_parse_error',
57f7e3c6 300 'compat_xpath',
2384f5a6 301 'compat_zip',
819e0531 302 'windows_enable_vt_mode',
e07e9313 303 'workaround_optparse_bug9161',
8c25f81b 304]