]> jfr.im git - yt-dlp.git/blame - yt_dlp/compat.py
[docs,cleanup] Some minor refactoring and improve docs
[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
8c25f81b 22import sys
c634ad2a 23import tokenize
24import urllib
25import xml.etree.ElementTree as etree
26from subprocess import DEVNULL
72b40955 27
72b40955 28
c634ad2a 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
32class compat_HTMLParseError(Exception):
33 pass
15707c7e 34
15707c7e 35
e6f21b3d 36# compat_ctypes_WINFUNCTYPE = ctypes.WINFUNCTYPE
37# will not work since ctypes.WINFUNCTYPE does not exist in UNIX machines
c634ad2a 38def compat_ctypes_WINFUNCTYPE(*args, **kwargs):
39 return ctypes.WINFUNCTYPE(*args, **kwargs)
eb7941e3
YCH
40
41
42class _TreeBuilder(etree.TreeBuilder):
43 def doctype(self, name, pubid, system):
44 pass
45
582be358 46
c634ad2a 47def compat_etree_fromstring(text):
48 return etree.XML(text, parser=etree.XMLParser(target=_TreeBuilder()))
8c25f81b 49
b08e235f
S
50
51compat_os_name = os._name if os.name == 'java' else os.name
52
53
54if compat_os_name == 'nt':
702ccf2d 55 def compat_shlex_quote(s):
b08e235f
S
56 return s if re.match(r'^[-_\w./]+$', s) else '"%s"' % s.replace('"', '\\"')
57else:
c634ad2a 58 from shlex import quote as compat_shlex_quote
51f579b6
S
59
60
8c25f81b 61def compat_ord(c):
5f6a1245
JW
62 if type(c) is int:
63 return c
64 else:
65 return ord(c)
8c25f81b
PH
66
67
c634ad2a 68def compat_setenv(key, value, env=os.environ):
69 env[key] = value
8c25f81b
PH
70
71
82fea5b4
S
72if 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
79else:
80 compat_realpath = os.path.realpath
81
82
c634ad2a 83def compat_print(s):
84 assert isinstance(s, compat_str)
85 print(s)
be4a824d
PH
86
87
067aa17e 88# Fix https://github.com/ytdl-org/youtube-dl/issues/4223
e07e9313
PH
89# See http://bugs.python.org/issue9161 for what is broken
90def workaround_optparse_bug9161():
07e378fa
PH
91 op = optparse.OptionParser()
92 og = optparse.OptionGroup(op, 'foo')
e07e9313 93 try:
07e378fa 94 og.add_option('-t')
b244b5c3 95 except TypeError:
e07e9313
PH
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
582be358 108
4a2f19ab
F
109try:
110 compat_Pattern = re.Pattern
111except AttributeError:
112 compat_Pattern = type(re.compile(''))
113
114
115try:
116 compat_Match = re.Match
117except AttributeError:
118 compat_Match = type(re.compile('').match(''))
119
120
e36d50c5 121try:
c634ad2a 122 compat_asyncio_run = asyncio.run # >= 3.7
e36d50c5 123except 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
c589c1d3 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
138if 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:]
149else:
150 compat_expanduser = os.path.expanduser
151
152
edf65256 153try:
154 from Cryptodome.Cipher import AES as compat_pycrypto_AES
155except ImportError:
156 try:
157 from Crypto.Cipher import AES as compat_pycrypto_AES
158 except ImportError:
159 compat_pycrypto_AES = None
160
161
c634ad2a 162# Deprecated
163
164compat_basestring = str
165compat_chr = chr
166compat_input = input
167compat_integer_types = (int, )
168compat_kwargs = lambda kwargs: kwargs
169compat_numeric_types = (int, float, complex)
170compat_str = str
171compat_xpath = lambda xpath: xpath
172compat_zip = zip
173
174compat_HTMLParser = html.parser.HTMLParser
175compat_HTTPError = urllib.error.HTTPError
176compat_Struct = struct.Struct
177compat_b64decode = base64.b64decode
178compat_cookiejar = http.cookiejar
179compat_cookiejar_Cookie = compat_cookiejar.Cookie
180compat_cookies = http.cookies
181compat_cookies_SimpleCookie = compat_cookies.SimpleCookie
182compat_etree_Element = etree.Element
183compat_etree_register_namespace = etree.register_namespace
c634ad2a 184compat_get_terminal_size = shutil.get_terminal_size
185compat_getenv = os.getenv
186compat_getpass = getpass.getpass
187compat_html_entities = html.entities
188compat_html_entities_html5 = compat_html_entities.html5
189compat_http_client = http.client
190compat_http_server = http.server
191compat_itertools_count = itertools.count
192compat_parse_qs = urllib.parse.parse_qs
193compat_shlex_split = shlex.split
194compat_socket_create_connection = socket.create_connection
195compat_struct_pack = struct.pack
196compat_struct_unpack = struct.unpack
197compat_subprocess_get_DEVNULL = lambda: DEVNULL
198compat_tokenize_tokenize = tokenize.tokenize
199compat_urllib_error = urllib.error
200compat_urllib_parse = urllib.parse
201compat_urllib_parse_quote = urllib.parse.quote
202compat_urllib_parse_quote_plus = urllib.parse.quote_plus
203compat_urllib_parse_unquote = urllib.parse.unquote
204compat_urllib_parse_unquote_plus = urllib.parse.unquote_plus
205compat_urllib_parse_unquote_to_bytes = urllib.parse.unquote_to_bytes
206compat_urllib_parse_urlencode = urllib.parse.urlencode
207compat_urllib_parse_urlparse = urllib.parse.urlparse
208compat_urllib_parse_urlunparse = urllib.parse.urlunparse
209compat_urllib_request = urllib.request
210compat_urllib_request_DataHandler = urllib.request.DataHandler
211compat_urllib_response = urllib.response
212compat_urlparse = urllib.parse
213compat_urlretrieve = urllib.request.urlretrieve
214compat_xml_parse_error = etree.ParseError
215
216
217# Set public objects
218
8c25f81b 219__all__ = [
b081f53b 220 'compat_HTMLParseError',
8bb56eee 221 'compat_HTMLParser',
8c25f81b 222 'compat_HTTPError',
4a2f19ab
F
223 'compat_Match',
224 'compat_Pattern',
65220c3b 225 'compat_Struct',
e36d50c5 226 'compat_asyncio_run',
f206126d 227 'compat_b64decode',
0196149c 228 'compat_basestring',
8c25f81b
PH
229 'compat_chr',
230 'compat_cookiejar',
6d874fee 231 'compat_cookiejar_Cookie',
799207e8 232 'compat_cookies',
f7ad7160 233 'compat_cookies_SimpleCookie',
d7cd9a9e 234 'compat_ctypes_WINFUNCTYPE',
399f7687 235 'compat_etree_Element',
36e6f62c 236 'compat_etree_fromstring',
da162c11 237 'compat_etree_register_namespace',
8c25f81b 238 'compat_expanduser',
003c69a8 239 'compat_get_terminal_size',
8c25f81b
PH
240 'compat_getenv',
241 'compat_getpass',
242 'compat_html_entities',
9631a94f 243 'compat_html_entities_html5',
8c25f81b 244 'compat_http_client',
83fda3c0 245 'compat_http_server',
e67f6880 246 'compat_input',
075a13d3 247 'compat_integer_types',
a0e060ac 248 'compat_itertools_count',
c7b0add8 249 'compat_kwargs',
28572a1a 250 'compat_numeric_types',
8c25f81b 251 'compat_ord',
e9c0cdd3 252 'compat_os_name',
8c25f81b
PH
253 'compat_parse_qs',
254 'compat_print',
edf65256 255 'compat_pycrypto_AES',
bfe2b8cf 256 'compat_realpath',
fe40f9ee 257 'compat_setenv',
702ccf2d 258 'compat_shlex_quote',
51f579b6 259 'compat_shlex_split',
be4a824d 260 'compat_socket_create_connection',
987493ae 261 'compat_str',
edaa23f8
YCH
262 'compat_struct_pack',
263 'compat_struct_unpack',
8c25f81b 264 'compat_subprocess_get_DEVNULL',
67134eab 265 'compat_tokenize_tokenize',
8c25f81b
PH
266 'compat_urllib_error',
267 'compat_urllib_parse',
732044af 268 'compat_urllib_parse_quote',
269 'compat_urllib_parse_quote_plus',
8c25f81b 270 'compat_urllib_parse_unquote',
aa99aa4e 271 'compat_urllib_parse_unquote_plus',
9fefc886 272 'compat_urllib_parse_unquote_to_bytes',
15707c7e 273 'compat_urllib_parse_urlencode',
8c25f81b 274 'compat_urllib_parse_urlparse',
732044af 275 'compat_urllib_parse_urlunparse',
8c25f81b 276 'compat_urllib_request',
0a67a363
YCH
277 'compat_urllib_request_DataHandler',
278 'compat_urllib_response',
8c25f81b
PH
279 'compat_urlparse',
280 'compat_urlretrieve',
281 'compat_xml_parse_error',
57f7e3c6 282 'compat_xpath',
2384f5a6 283 'compat_zip',
e07e9313 284 'workaround_optparse_bug9161',
8c25f81b 285]