]> jfr.im git - yt-dlp.git/blob - yt_dlp/compat.py
[docs,cleanup] Some minor refactoring and improve docs
[yt-dlp.git] / yt_dlp / compat.py
1 # coding: utf-8
2
3 import asyncio
4 import base64
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 optparse
16 import os
17 import re
18 import shlex
19 import shutil
20 import socket
21 import struct
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
59
60
61 def compat_ord(c):
62 if type(c) is int:
63 return c
64 else:
65 return ord(c)
66
67
68 def compat_setenv(key, value, env=os.environ):
69 env[key] = value
70
71
72 if compat_os_name == 'nt' and sys.version_info < (3, 8):
73 # os.path.realpath on Windows does not follow symbolic links
74 # prior to Python 3.8 (see https://bugs.python.org/issue9949)
75 def compat_realpath(path):
76 while os.path.islink(path):
77 path = os.path.abspath(os.readlink(path))
78 return path
79 else:
80 compat_realpath = os.path.realpath
81
82
83 def compat_print(s):
84 assert isinstance(s, compat_str)
85 print(s)
86
87
88 # Fix https://github.com/ytdl-org/youtube-dl/issues/4223
89 # See http://bugs.python.org/issue9161 for what is broken
90 def workaround_optparse_bug9161():
91 op = optparse.OptionParser()
92 og = optparse.OptionGroup(op, 'foo')
93 try:
94 og.add_option('-t')
95 except TypeError:
96 real_add_option = optparse.OptionGroup.add_option
97
98 def _compat_add_option(self, *args, **kwargs):
99 enc = lambda v: (
100 v.encode('ascii', 'replace') if isinstance(v, compat_str)
101 else v)
102 bargs = [enc(a) for a in args]
103 bkwargs = dict(
104 (k, enc(v)) for k, v in kwargs.items())
105 return real_add_option(self, *bargs, **bkwargs)
106 optparse.OptionGroup.add_option = _compat_add_option
107
108
109 try:
110 compat_Pattern = re.Pattern
111 except AttributeError:
112 compat_Pattern = type(re.compile(''))
113
114
115 try:
116 compat_Match = re.Match
117 except AttributeError:
118 compat_Match = type(re.compile('').match(''))
119
120
121 try:
122 compat_asyncio_run = asyncio.run # >= 3.7
123 except AttributeError:
124 def compat_asyncio_run(coro):
125 try:
126 loop = asyncio.get_event_loop()
127 except RuntimeError:
128 loop = asyncio.new_event_loop()
129 asyncio.set_event_loop(loop)
130 loop.run_until_complete(coro)
131
132 asyncio.run = compat_asyncio_run
133
134
135 # Python 3.8+ does not honor %HOME% on windows, but this breaks compatibility with youtube-dl
136 # See https://github.com/yt-dlp/yt-dlp/issues/792
137 # https://docs.python.org/3/library/os.path.html#os.path.expanduser
138 if compat_os_name in ('nt', 'ce') and 'HOME' in os.environ:
139 _userhome = os.environ['HOME']
140
141 def compat_expanduser(path):
142 if not path.startswith('~'):
143 return path
144 i = path.replace('\\', '/', 1).find('/') # ~user
145 if i < 0:
146 i = len(path)
147 userhome = os.path.join(os.path.dirname(_userhome), path[1:i]) if i > 1 else _userhome
148 return userhome + path[i:]
149 else:
150 compat_expanduser = os.path.expanduser
151
152
153 try:
154 from Cryptodome.Cipher import AES as compat_pycrypto_AES
155 except ImportError:
156 try:
157 from Crypto.Cipher import AES as compat_pycrypto_AES
158 except ImportError:
159 compat_pycrypto_AES = None
160
161
162 # Deprecated
163
164 compat_basestring = str
165 compat_chr = chr
166 compat_input = input
167 compat_integer_types = (int, )
168 compat_kwargs = lambda kwargs: kwargs
169 compat_numeric_types = (int, float, complex)
170 compat_str = str
171 compat_xpath = lambda xpath: xpath
172 compat_zip = zip
173
174 compat_HTMLParser = html.parser.HTMLParser
175 compat_HTTPError = urllib.error.HTTPError
176 compat_Struct = struct.Struct
177 compat_b64decode = base64.b64decode
178 compat_cookiejar = http.cookiejar
179 compat_cookiejar_Cookie = compat_cookiejar.Cookie
180 compat_cookies = http.cookies
181 compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
182 compat_etree_Element = etree.Element
183 compat_etree_register_namespace = etree.register_namespace
184 compat_get_terminal_size = shutil.get_terminal_size
185 compat_getenv = os.getenv
186 compat_getpass = getpass.getpass
187 compat_html_entities = html.entities
188 compat_html_entities_html5 = compat_html_entities.html5
189 compat_http_client = http.client
190 compat_http_server = http.server
191 compat_itertools_count = itertools.count
192 compat_parse_qs = urllib.parse.parse_qs
193 compat_shlex_split = shlex.split
194 compat_socket_create_connection = socket.create_connection
195 compat_struct_pack = struct.pack
196 compat_struct_unpack = struct.unpack
197 compat_subprocess_get_DEVNULL = lambda: DEVNULL
198 compat_tokenize_tokenize = tokenize.tokenize
199 compat_urllib_error = urllib.error
200 compat_urllib_parse = urllib.parse
201 compat_urllib_parse_quote = urllib.parse.quote
202 compat_urllib_parse_quote_plus = urllib.parse.quote_plus
203 compat_urllib_parse_unquote = urllib.parse.unquote
204 compat_urllib_parse_unquote_plus = urllib.parse.unquote_plus
205 compat_urllib_parse_unquote_to_bytes = urllib.parse.unquote_to_bytes
206 compat_urllib_parse_urlencode = urllib.parse.urlencode
207 compat_urllib_parse_urlparse = urllib.parse.urlparse
208 compat_urllib_parse_urlunparse = urllib.parse.urlunparse
209 compat_urllib_request = urllib.request
210 compat_urllib_request_DataHandler = urllib.request.DataHandler
211 compat_urllib_response = urllib.response
212 compat_urlparse = urllib.parse
213 compat_urlretrieve = urllib.request.urlretrieve
214 compat_xml_parse_error = etree.ParseError
215
216
217 # Set public objects
218
219 __all__ = [
220 'compat_HTMLParseError',
221 'compat_HTMLParser',
222 'compat_HTTPError',
223 'compat_Match',
224 'compat_Pattern',
225 'compat_Struct',
226 'compat_asyncio_run',
227 'compat_b64decode',
228 'compat_basestring',
229 'compat_chr',
230 'compat_cookiejar',
231 'compat_cookiejar_Cookie',
232 'compat_cookies',
233 'compat_cookies_SimpleCookie',
234 'compat_ctypes_WINFUNCTYPE',
235 'compat_etree_Element',
236 'compat_etree_fromstring',
237 'compat_etree_register_namespace',
238 'compat_expanduser',
239 'compat_get_terminal_size',
240 'compat_getenv',
241 'compat_getpass',
242 'compat_html_entities',
243 'compat_html_entities_html5',
244 'compat_http_client',
245 'compat_http_server',
246 'compat_input',
247 'compat_integer_types',
248 'compat_itertools_count',
249 'compat_kwargs',
250 'compat_numeric_types',
251 'compat_ord',
252 'compat_os_name',
253 'compat_parse_qs',
254 'compat_print',
255 'compat_pycrypto_AES',
256 'compat_realpath',
257 'compat_setenv',
258 'compat_shlex_quote',
259 'compat_shlex_split',
260 'compat_socket_create_connection',
261 'compat_str',
262 'compat_struct_pack',
263 'compat_struct_unpack',
264 'compat_subprocess_get_DEVNULL',
265 'compat_tokenize_tokenize',
266 'compat_urllib_error',
267 'compat_urllib_parse',
268 'compat_urllib_parse_quote',
269 'compat_urllib_parse_quote_plus',
270 'compat_urllib_parse_unquote',
271 'compat_urllib_parse_unquote_plus',
272 'compat_urllib_parse_unquote_to_bytes',
273 'compat_urllib_parse_urlencode',
274 'compat_urllib_parse_urlparse',
275 'compat_urllib_parse_urlunparse',
276 'compat_urllib_request',
277 'compat_urllib_request_DataHandler',
278 'compat_urllib_response',
279 'compat_urlparse',
280 'compat_urlretrieve',
281 'compat_xml_parse_error',
282 'compat_xpath',
283 'compat_zip',
284 'workaround_optparse_bug9161',
285 ]