]> jfr.im git - yt-dlp.git/blame - yt_dlp/compat.py
[utils] Add `join_nonempty`
[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
819e0531 163def windows_enable_vt_mode(): # TODO: Do this the proper way https://bugs.python.org/issue30075
164 if compat_os_name != 'nt':
165 return
673944b0 166 startupinfo = subprocess.STARTUPINFO()
167 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
168 subprocess.Popen('', shell=True, startupinfo=startupinfo)
819e0531 169
170
c634ad2a 171# Deprecated
172
173compat_basestring = str
174compat_chr = chr
175compat_input = input
176compat_integer_types = (int, )
177compat_kwargs = lambda kwargs: kwargs
178compat_numeric_types = (int, float, complex)
179compat_str = str
180compat_xpath = lambda xpath: xpath
181compat_zip = zip
182
183compat_HTMLParser = html.parser.HTMLParser
184compat_HTTPError = urllib.error.HTTPError
185compat_Struct = struct.Struct
186compat_b64decode = base64.b64decode
187compat_cookiejar = http.cookiejar
188compat_cookiejar_Cookie = compat_cookiejar.Cookie
189compat_cookies = http.cookies
190compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
191compat_etree_Element = etree.Element
192compat_etree_register_namespace = etree.register_namespace
c634ad2a 193compat_get_terminal_size = shutil.get_terminal_size
194compat_getenv = os.getenv
195compat_getpass = getpass.getpass
196compat_html_entities = html.entities
197compat_html_entities_html5 = compat_html_entities.html5
198compat_http_client = http.client
199compat_http_server = http.server
200compat_itertools_count = itertools.count
201compat_parse_qs = urllib.parse.parse_qs
202compat_shlex_split = shlex.split
203compat_socket_create_connection = socket.create_connection
204compat_struct_pack = struct.pack
205compat_struct_unpack = struct.unpack
206compat_subprocess_get_DEVNULL = lambda: DEVNULL
207compat_tokenize_tokenize = tokenize.tokenize
208compat_urllib_error = urllib.error
209compat_urllib_parse = urllib.parse
210compat_urllib_parse_quote = urllib.parse.quote
211compat_urllib_parse_quote_plus = urllib.parse.quote_plus
212compat_urllib_parse_unquote = urllib.parse.unquote
213compat_urllib_parse_unquote_plus = urllib.parse.unquote_plus
214compat_urllib_parse_unquote_to_bytes = urllib.parse.unquote_to_bytes
215compat_urllib_parse_urlencode = urllib.parse.urlencode
216compat_urllib_parse_urlparse = urllib.parse.urlparse
217compat_urllib_parse_urlunparse = urllib.parse.urlunparse
218compat_urllib_request = urllib.request
219compat_urllib_request_DataHandler = urllib.request.DataHandler
220compat_urllib_response = urllib.response
221compat_urlparse = urllib.parse
222compat_urlretrieve = urllib.request.urlretrieve
223compat_xml_parse_error = etree.ParseError
224
225
226# Set public objects
227
8c25f81b 228__all__ = [
b081f53b 229 'compat_HTMLParseError',
8bb56eee 230 'compat_HTMLParser',
8c25f81b 231 'compat_HTTPError',
4a2f19ab
F
232 'compat_Match',
233 'compat_Pattern',
65220c3b 234 'compat_Struct',
e36d50c5 235 'compat_asyncio_run',
f206126d 236 'compat_b64decode',
0196149c 237 'compat_basestring',
8c25f81b
PH
238 'compat_chr',
239 'compat_cookiejar',
6d874fee 240 'compat_cookiejar_Cookie',
799207e8 241 'compat_cookies',
f7ad7160 242 'compat_cookies_SimpleCookie',
d7cd9a9e 243 'compat_ctypes_WINFUNCTYPE',
399f7687 244 'compat_etree_Element',
36e6f62c 245 'compat_etree_fromstring',
da162c11 246 'compat_etree_register_namespace',
8c25f81b 247 'compat_expanduser',
003c69a8 248 'compat_get_terminal_size',
8c25f81b
PH
249 'compat_getenv',
250 'compat_getpass',
251 'compat_html_entities',
9631a94f 252 'compat_html_entities_html5',
8c25f81b 253 'compat_http_client',
83fda3c0 254 'compat_http_server',
e67f6880 255 'compat_input',
075a13d3 256 'compat_integer_types',
a0e060ac 257 'compat_itertools_count',
c7b0add8 258 'compat_kwargs',
28572a1a 259 'compat_numeric_types',
8c25f81b 260 'compat_ord',
e9c0cdd3 261 'compat_os_name',
8c25f81b
PH
262 'compat_parse_qs',
263 'compat_print',
edf65256 264 'compat_pycrypto_AES',
bfe2b8cf 265 'compat_realpath',
fe40f9ee 266 'compat_setenv',
702ccf2d 267 'compat_shlex_quote',
51f579b6 268 'compat_shlex_split',
be4a824d 269 'compat_socket_create_connection',
987493ae 270 'compat_str',
edaa23f8
YCH
271 'compat_struct_pack',
272 'compat_struct_unpack',
8c25f81b 273 'compat_subprocess_get_DEVNULL',
67134eab 274 'compat_tokenize_tokenize',
8c25f81b
PH
275 'compat_urllib_error',
276 'compat_urllib_parse',
732044af 277 'compat_urllib_parse_quote',
278 'compat_urllib_parse_quote_plus',
8c25f81b 279 'compat_urllib_parse_unquote',
aa99aa4e 280 'compat_urllib_parse_unquote_plus',
9fefc886 281 'compat_urllib_parse_unquote_to_bytes',
15707c7e 282 'compat_urllib_parse_urlencode',
8c25f81b 283 'compat_urllib_parse_urlparse',
732044af 284 'compat_urllib_parse_urlunparse',
8c25f81b 285 'compat_urllib_request',
0a67a363
YCH
286 'compat_urllib_request_DataHandler',
287 'compat_urllib_response',
8c25f81b
PH
288 'compat_urlparse',
289 'compat_urlretrieve',
290 'compat_xml_parse_error',
57f7e3c6 291 'compat_xpath',
2384f5a6 292 'compat_zip',
819e0531 293 'windows_enable_vt_mode',
e07e9313 294 'workaround_optparse_bug9161',
8c25f81b 295]