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