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