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