##// END OF EJS Templates
pycompat: alias urllib symbols directly...
Gregory Szorc -
r31399:1ed169c5 default
parent child Browse files
Show More
@@ -1,309 +1,315 b''
1 # pycompat.py - portability shim for python 3
1 # pycompat.py - portability shim for python 3
2 #
2 #
3 # This software may be used and distributed according to the terms of the
3 # This software may be used and distributed according to the terms of the
4 # GNU General Public License version 2 or any later version.
4 # GNU General Public License version 2 or any later version.
5
5
6 """Mercurial portability shim for python 3.
6 """Mercurial portability shim for python 3.
7
7
8 This contains aliases to hide python version-specific details from the core.
8 This contains aliases to hide python version-specific details from the core.
9 """
9 """
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12
12
13 import getopt
13 import getopt
14 import os
14 import os
15 import shlex
15 import shlex
16 import sys
16 import sys
17
17
18 ispy3 = (sys.version_info[0] >= 3)
18 ispy3 = (sys.version_info[0] >= 3)
19
19
20 if not ispy3:
20 if not ispy3:
21 import cPickle as pickle
21 import cPickle as pickle
22 import httplib
22 import httplib
23 import Queue as _queue
23 import Queue as _queue
24 import SocketServer as socketserver
24 import SocketServer as socketserver
25 import urlparse
25 import urlparse
26 urlunquote = urlparse.unquote
26 urlunquote = urlparse.unquote
27 import xmlrpclib
27 import xmlrpclib
28 else:
28 else:
29 import http.client as httplib
29 import http.client as httplib
30 import pickle
30 import pickle
31 import queue as _queue
31 import queue as _queue
32 import socketserver
32 import socketserver
33 import urllib.parse as urlparse
33 import urllib.parse as urlparse
34 urlunquote = urlparse.unquote_to_bytes
34 urlunquote = urlparse.unquote_to_bytes
35 import xmlrpc.client as xmlrpclib
35 import xmlrpc.client as xmlrpclib
36
36
37 if ispy3:
37 if ispy3:
38 import builtins
38 import builtins
39 import functools
39 import functools
40 import io
40 import io
41
41
42 fsencode = os.fsencode
42 fsencode = os.fsencode
43 fsdecode = os.fsdecode
43 fsdecode = os.fsdecode
44 # A bytes version of os.name.
44 # A bytes version of os.name.
45 osname = os.name.encode('ascii')
45 osname = os.name.encode('ascii')
46 ospathsep = os.pathsep.encode('ascii')
46 ospathsep = os.pathsep.encode('ascii')
47 ossep = os.sep.encode('ascii')
47 ossep = os.sep.encode('ascii')
48 osaltsep = os.altsep
48 osaltsep = os.altsep
49 if osaltsep:
49 if osaltsep:
50 osaltsep = osaltsep.encode('ascii')
50 osaltsep = osaltsep.encode('ascii')
51 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
51 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # returns bytes.
52 # returns bytes.
53 getcwd = os.getcwdb
53 getcwd = os.getcwdb
54 sysplatform = sys.platform.encode('ascii')
54 sysplatform = sys.platform.encode('ascii')
55 sysexecutable = sys.executable
55 sysexecutable = sys.executable
56 if sysexecutable:
56 if sysexecutable:
57 sysexecutable = os.fsencode(sysexecutable)
57 sysexecutable = os.fsencode(sysexecutable)
58 stringio = io.BytesIO
58 stringio = io.BytesIO
59
59
60 # TODO: .buffer might not exist if std streams were replaced; we'll need
60 # TODO: .buffer might not exist if std streams were replaced; we'll need
61 # a silly wrapper to make a bytes stream backed by a unicode one.
61 # a silly wrapper to make a bytes stream backed by a unicode one.
62 stdin = sys.stdin.buffer
62 stdin = sys.stdin.buffer
63 stdout = sys.stdout.buffer
63 stdout = sys.stdout.buffer
64 stderr = sys.stderr.buffer
64 stderr = sys.stderr.buffer
65
65
66 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
66 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
67 # we can use os.fsencode() to get back bytes argv.
67 # we can use os.fsencode() to get back bytes argv.
68 #
68 #
69 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
69 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
70 #
70 #
71 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
71 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
72 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
72 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
73 if getattr(sys, 'argv', None) is not None:
73 if getattr(sys, 'argv', None) is not None:
74 sysargv = list(map(os.fsencode, sys.argv))
74 sysargv = list(map(os.fsencode, sys.argv))
75
75
76 def bytechr(i):
76 def bytechr(i):
77 return bytes([i])
77 return bytes([i])
78
78
79 def iterbytestr(s):
79 def iterbytestr(s):
80 """Iterate bytes as if it were a str object of Python 2"""
80 """Iterate bytes as if it were a str object of Python 2"""
81 return iter(s[i:i + 1] for i in range(len(s)))
81 return iter(s[i:i + 1] for i in range(len(s)))
82
82
83 def sysstr(s):
83 def sysstr(s):
84 """Return a keyword str to be passed to Python functions such as
84 """Return a keyword str to be passed to Python functions such as
85 getattr() and str.encode()
85 getattr() and str.encode()
86
86
87 This never raises UnicodeDecodeError. Non-ascii characters are
87 This never raises UnicodeDecodeError. Non-ascii characters are
88 considered invalid and mapped to arbitrary but unique code points
88 considered invalid and mapped to arbitrary but unique code points
89 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
89 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
90 """
90 """
91 if isinstance(s, builtins.str):
91 if isinstance(s, builtins.str):
92 return s
92 return s
93 return s.decode(u'latin-1')
93 return s.decode(u'latin-1')
94
94
95 def _wrapattrfunc(f):
95 def _wrapattrfunc(f):
96 @functools.wraps(f)
96 @functools.wraps(f)
97 def w(object, name, *args):
97 def w(object, name, *args):
98 return f(object, sysstr(name), *args)
98 return f(object, sysstr(name), *args)
99 return w
99 return w
100
100
101 # these wrappers are automagically imported by hgloader
101 # these wrappers are automagically imported by hgloader
102 delattr = _wrapattrfunc(builtins.delattr)
102 delattr = _wrapattrfunc(builtins.delattr)
103 getattr = _wrapattrfunc(builtins.getattr)
103 getattr = _wrapattrfunc(builtins.getattr)
104 hasattr = _wrapattrfunc(builtins.hasattr)
104 hasattr = _wrapattrfunc(builtins.hasattr)
105 setattr = _wrapattrfunc(builtins.setattr)
105 setattr = _wrapattrfunc(builtins.setattr)
106 xrange = builtins.range
106 xrange = builtins.range
107
107
108 def open(name, mode='r', buffering=-1):
108 def open(name, mode='r', buffering=-1):
109 return builtins.open(name, sysstr(mode), buffering)
109 return builtins.open(name, sysstr(mode), buffering)
110
110
111 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
111 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
112 # pass bytes there. Passing unicodes will result in unicodes as return
112 # pass bytes there. Passing unicodes will result in unicodes as return
113 # values which we need to convert again to bytes.
113 # values which we need to convert again to bytes.
114 def getoptb(args, shortlist, namelist):
114 def getoptb(args, shortlist, namelist):
115 args = [a.decode('latin-1') for a in args]
115 args = [a.decode('latin-1') for a in args]
116 shortlist = shortlist.decode('latin-1')
116 shortlist = shortlist.decode('latin-1')
117 namelist = [a.decode('latin-1') for a in namelist]
117 namelist = [a.decode('latin-1') for a in namelist]
118 opts, args = getopt.getopt(args, shortlist, namelist)
118 opts, args = getopt.getopt(args, shortlist, namelist)
119 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
119 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
120 for a in opts]
120 for a in opts]
121 args = [a.encode('latin-1') for a in args]
121 args = [a.encode('latin-1') for a in args]
122 return opts, args
122 return opts, args
123
123
124 # keys of keyword arguments in Python need to be strings which are unicodes
124 # keys of keyword arguments in Python need to be strings which are unicodes
125 # Python 3. This function takes keyword arguments, convert the keys to str.
125 # Python 3. This function takes keyword arguments, convert the keys to str.
126 def strkwargs(dic):
126 def strkwargs(dic):
127 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
127 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
128 return dic
128 return dic
129
129
130 # keys of keyword arguments need to be unicode while passing into
130 # keys of keyword arguments need to be unicode while passing into
131 # a function. This function helps us to convert those keys back to bytes
131 # a function. This function helps us to convert those keys back to bytes
132 # again as we need to deal with bytes.
132 # again as we need to deal with bytes.
133 def byteskwargs(dic):
133 def byteskwargs(dic):
134 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
134 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
135 return dic
135 return dic
136
136
137 # shlex.split() accepts unicodes on Python 3. This function takes bytes
137 # shlex.split() accepts unicodes on Python 3. This function takes bytes
138 # argument, convert it into unicodes, pass into shlex.split(), convert the
138 # argument, convert it into unicodes, pass into shlex.split(), convert the
139 # returned value to bytes and return that.
139 # returned value to bytes and return that.
140 # TODO: handle shlex.shlex().
140 # TODO: handle shlex.shlex().
141 def shlexsplit(s):
141 def shlexsplit(s):
142 ret = shlex.split(s.decode('latin-1'))
142 ret = shlex.split(s.decode('latin-1'))
143 return [a.encode('latin-1') for a in ret]
143 return [a.encode('latin-1') for a in ret]
144
144
145 else:
145 else:
146 import cStringIO
146 import cStringIO
147
147
148 bytechr = chr
148 bytechr = chr
149 iterbytestr = iter
149 iterbytestr = iter
150
150
151 def sysstr(s):
151 def sysstr(s):
152 return s
152 return s
153
153
154 # Partial backport from os.py in Python 3, which only accepts bytes.
154 # Partial backport from os.py in Python 3, which only accepts bytes.
155 # In Python 2, our paths should only ever be bytes, a unicode path
155 # In Python 2, our paths should only ever be bytes, a unicode path
156 # indicates a bug.
156 # indicates a bug.
157 def fsencode(filename):
157 def fsencode(filename):
158 if isinstance(filename, str):
158 if isinstance(filename, str):
159 return filename
159 return filename
160 else:
160 else:
161 raise TypeError(
161 raise TypeError(
162 "expect str, not %s" % type(filename).__name__)
162 "expect str, not %s" % type(filename).__name__)
163
163
164 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
164 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
165 # better not to touch Python 2 part as it's already working fine.
165 # better not to touch Python 2 part as it's already working fine.
166 def fsdecode(filename):
166 def fsdecode(filename):
167 return filename
167 return filename
168
168
169 def getoptb(args, shortlist, namelist):
169 def getoptb(args, shortlist, namelist):
170 return getopt.getopt(args, shortlist, namelist)
170 return getopt.getopt(args, shortlist, namelist)
171
171
172 def strkwargs(dic):
172 def strkwargs(dic):
173 return dic
173 return dic
174
174
175 def byteskwargs(dic):
175 def byteskwargs(dic):
176 return dic
176 return dic
177
177
178 osname = os.name
178 osname = os.name
179 ospathsep = os.pathsep
179 ospathsep = os.pathsep
180 ossep = os.sep
180 ossep = os.sep
181 osaltsep = os.altsep
181 osaltsep = os.altsep
182 stdin = sys.stdin
182 stdin = sys.stdin
183 stdout = sys.stdout
183 stdout = sys.stdout
184 stderr = sys.stderr
184 stderr = sys.stderr
185 if getattr(sys, 'argv', None) is not None:
185 if getattr(sys, 'argv', None) is not None:
186 sysargv = sys.argv
186 sysargv = sys.argv
187 sysplatform = sys.platform
187 sysplatform = sys.platform
188 getcwd = os.getcwd
188 getcwd = os.getcwd
189 sysexecutable = sys.executable
189 sysexecutable = sys.executable
190 shlexsplit = shlex.split
190 shlexsplit = shlex.split
191 stringio = cStringIO.StringIO
191 stringio = cStringIO.StringIO
192
192
193 empty = _queue.Empty
193 empty = _queue.Empty
194 queue = _queue.Queue
194 queue = _queue.Queue
195
195
196 class _pycompatstub(object):
196 class _pycompatstub(object):
197 def __init__(self):
197 def __init__(self):
198 self._aliases = {}
198 self._aliases = {}
199
199
200 def _registeraliases(self, origin, items):
200 def _registeraliases(self, origin, items):
201 """Add items that will be populated at the first access"""
201 """Add items that will be populated at the first access"""
202 items = map(sysstr, items)
202 items = map(sysstr, items)
203 self._aliases.update(
203 self._aliases.update(
204 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
204 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
205 for item in items)
205 for item in items)
206
206
207 def __getattr__(self, name):
207 def __getattr__(self, name):
208 try:
208 try:
209 origin, item = self._aliases[name]
209 origin, item = self._aliases[name]
210 except KeyError:
210 except KeyError:
211 raise AttributeError(name)
211 raise AttributeError(name)
212 self.__dict__[name] = obj = getattr(origin, item)
212 self.__dict__[name] = obj = getattr(origin, item)
213 return obj
213 return obj
214
214
215 httpserver = _pycompatstub()
215 httpserver = _pycompatstub()
216 urlreq = _pycompatstub()
216 urlreq = _pycompatstub()
217 urlerr = _pycompatstub()
217 urlerr = _pycompatstub()
218 if not ispy3:
218 if not ispy3:
219 import BaseHTTPServer
219 import BaseHTTPServer
220 import CGIHTTPServer
220 import CGIHTTPServer
221 import SimpleHTTPServer
221 import SimpleHTTPServer
222 import urllib2
222 import urllib2
223 import urllib
223 import urllib
224 urlreq._registeraliases(urllib, (
224 urlreq._registeraliases(urllib, (
225 "addclosehook",
225 "addclosehook",
226 "addinfourl",
226 "addinfourl",
227 "ftpwrapper",
227 "ftpwrapper",
228 "pathname2url",
228 "pathname2url",
229 "quote",
229 "quote",
230 "splitattr",
230 "splitattr",
231 "splitpasswd",
231 "splitpasswd",
232 "splitport",
232 "splitport",
233 "splituser",
233 "splituser",
234 "unquote",
234 "unquote",
235 "url2pathname",
235 "url2pathname",
236 "urlencode",
236 "urlencode",
237 ))
237 ))
238 urlreq._registeraliases(urllib2, (
238 urlreq._registeraliases(urllib2, (
239 "AbstractHTTPHandler",
239 "AbstractHTTPHandler",
240 "BaseHandler",
240 "BaseHandler",
241 "build_opener",
241 "build_opener",
242 "FileHandler",
242 "FileHandler",
243 "FTPHandler",
243 "FTPHandler",
244 "HTTPBasicAuthHandler",
244 "HTTPBasicAuthHandler",
245 "HTTPDigestAuthHandler",
245 "HTTPDigestAuthHandler",
246 "HTTPHandler",
246 "HTTPHandler",
247 "HTTPPasswordMgrWithDefaultRealm",
247 "HTTPPasswordMgrWithDefaultRealm",
248 "HTTPSHandler",
248 "HTTPSHandler",
249 "install_opener",
249 "install_opener",
250 "ProxyHandler",
250 "ProxyHandler",
251 "Request",
251 "Request",
252 "urlopen",
252 "urlopen",
253 ))
253 ))
254 urlerr._registeraliases(urllib2, (
254 urlerr._registeraliases(urllib2, (
255 "HTTPError",
255 "HTTPError",
256 "URLError",
256 "URLError",
257 ))
257 ))
258 httpserver._registeraliases(BaseHTTPServer, (
258 httpserver._registeraliases(BaseHTTPServer, (
259 "HTTPServer",
259 "HTTPServer",
260 "BaseHTTPRequestHandler",
260 "BaseHTTPRequestHandler",
261 ))
261 ))
262 httpserver._registeraliases(SimpleHTTPServer, (
262 httpserver._registeraliases(SimpleHTTPServer, (
263 "SimpleHTTPRequestHandler",
263 "SimpleHTTPRequestHandler",
264 ))
264 ))
265 httpserver._registeraliases(CGIHTTPServer, (
265 httpserver._registeraliases(CGIHTTPServer, (
266 "CGIHTTPRequestHandler",
266 "CGIHTTPRequestHandler",
267 ))
267 ))
268
268
269 else:
269 else:
270 import urllib.parse
271 urlreq._registeraliases(urllib.parse, (
272 "quote",
273 "splitattr",
274 "splitpasswd",
275 "splitport",
276 "splituser",
277 "unquote",
278 ))
270 import urllib.request
279 import urllib.request
271 urlreq._registeraliases(urllib.request, (
280 urlreq._registeraliases(urllib.request, (
272 "AbstractHTTPHandler",
281 "AbstractHTTPHandler",
273 "addclosehook",
274 "addinfourl",
275 "BaseHandler",
282 "BaseHandler",
276 "build_opener",
283 "build_opener",
277 "FileHandler",
284 "FileHandler",
278 "FTPHandler",
285 "FTPHandler",
279 "ftpwrapper",
286 "ftpwrapper",
280 "HTTPHandler",
287 "HTTPHandler",
281 "HTTPSHandler",
288 "HTTPSHandler",
282 "install_opener",
289 "install_opener",
283 "pathname2url",
290 "pathname2url",
284 "HTTPBasicAuthHandler",
291 "HTTPBasicAuthHandler",
285 "HTTPDigestAuthHandler",
292 "HTTPDigestAuthHandler",
286 "HTTPPasswordMgrWithDefaultRealm",
293 "HTTPPasswordMgrWithDefaultRealm",
287 "ProxyHandler",
294 "ProxyHandler",
288 "quote",
289 "Request",
295 "Request",
290 "splitattr",
291 "splitpasswd",
292 "splitport",
293 "splituser",
294 "unquote",
295 "url2pathname",
296 "url2pathname",
296 "urlopen",
297 "urlopen",
297 ))
298 ))
299 import urllib.response
300 urlreq._registeraliases(urllib.response, (
301 "addclosehook",
302 "addinfourl",
303 ))
298 import urllib.error
304 import urllib.error
299 urlerr._registeraliases(urllib.error, (
305 urlerr._registeraliases(urllib.error, (
300 "HTTPError",
306 "HTTPError",
301 "URLError",
307 "URLError",
302 ))
308 ))
303 import http.server
309 import http.server
304 httpserver._registeraliases(http.server, (
310 httpserver._registeraliases(http.server, (
305 "HTTPServer",
311 "HTTPServer",
306 "BaseHTTPRequestHandler",
312 "BaseHTTPRequestHandler",
307 "SimpleHTTPRequestHandler",
313 "SimpleHTTPRequestHandler",
308 "CGIHTTPRequestHandler",
314 "CGIHTTPRequestHandler",
309 ))
315 ))
General Comments 0
You need to be logged in to leave comments. Login now