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