##// END OF EJS Templates
pycompat: add helper to iterate each char in bytes
Yuya Nishihara -
r31382:c9fd842d default
parent child Browse files
Show More
@@ -1,304 +1,309
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):
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)))
82
79 def sysstr(s):
83 def sysstr(s):
80 """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
81 getattr() and str.encode()
85 getattr() and str.encode()
82
86
83 This never raises UnicodeDecodeError. Non-ascii characters are
87 This never raises UnicodeDecodeError. Non-ascii characters are
84 considered invalid and mapped to arbitrary but unique code points
88 considered invalid and mapped to arbitrary but unique code points
85 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
89 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
86 """
90 """
87 if isinstance(s, builtins.str):
91 if isinstance(s, builtins.str):
88 return s
92 return s
89 return s.decode(u'latin-1')
93 return s.decode(u'latin-1')
90
94
91 def _wrapattrfunc(f):
95 def _wrapattrfunc(f):
92 @functools.wraps(f)
96 @functools.wraps(f)
93 def w(object, name, *args):
97 def w(object, name, *args):
94 return f(object, sysstr(name), *args)
98 return f(object, sysstr(name), *args)
95 return w
99 return w
96
100
97 # these wrappers are automagically imported by hgloader
101 # these wrappers are automagically imported by hgloader
98 delattr = _wrapattrfunc(builtins.delattr)
102 delattr = _wrapattrfunc(builtins.delattr)
99 getattr = _wrapattrfunc(builtins.getattr)
103 getattr = _wrapattrfunc(builtins.getattr)
100 hasattr = _wrapattrfunc(builtins.hasattr)
104 hasattr = _wrapattrfunc(builtins.hasattr)
101 setattr = _wrapattrfunc(builtins.setattr)
105 setattr = _wrapattrfunc(builtins.setattr)
102 xrange = builtins.range
106 xrange = builtins.range
103
107
104 def open(name, mode='r', buffering=-1):
108 def open(name, mode='r', buffering=-1):
105 return builtins.open(name, sysstr(mode), buffering)
109 return builtins.open(name, sysstr(mode), buffering)
106
110
107 # 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
108 # pass bytes there. Passing unicodes will result in unicodes as return
112 # pass bytes there. Passing unicodes will result in unicodes as return
109 # values which we need to convert again to bytes.
113 # values which we need to convert again to bytes.
110 def getoptb(args, shortlist, namelist):
114 def getoptb(args, shortlist, namelist):
111 args = [a.decode('latin-1') for a in args]
115 args = [a.decode('latin-1') for a in args]
112 shortlist = shortlist.decode('latin-1')
116 shortlist = shortlist.decode('latin-1')
113 namelist = [a.decode('latin-1') for a in namelist]
117 namelist = [a.decode('latin-1') for a in namelist]
114 opts, args = getopt.getopt(args, shortlist, namelist)
118 opts, args = getopt.getopt(args, shortlist, namelist)
115 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
119 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
116 for a in opts]
120 for a in opts]
117 args = [a.encode('latin-1') for a in args]
121 args = [a.encode('latin-1') for a in args]
118 return opts, args
122 return opts, args
119
123
120 # 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
121 # 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.
122 def strkwargs(dic):
126 def strkwargs(dic):
123 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())
124 return dic
128 return dic
125
129
126 # keys of keyword arguments need to be unicode while passing into
130 # keys of keyword arguments need to be unicode while passing into
127 # 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
128 # again as we need to deal with bytes.
132 # again as we need to deal with bytes.
129 def byteskwargs(dic):
133 def byteskwargs(dic):
130 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())
131 return dic
135 return dic
132
136
133 # shlex.split() accepts unicodes on Python 3. This function takes bytes
137 # shlex.split() accepts unicodes on Python 3. This function takes bytes
134 # argument, convert it into unicodes, pass into shlex.split(), convert the
138 # argument, convert it into unicodes, pass into shlex.split(), convert the
135 # returned value to bytes and return that.
139 # returned value to bytes and return that.
136 # TODO: handle shlex.shlex().
140 # TODO: handle shlex.shlex().
137 def shlexsplit(s):
141 def shlexsplit(s):
138 ret = shlex.split(s.decode('latin-1'))
142 ret = shlex.split(s.decode('latin-1'))
139 return [a.encode('latin-1') for a in ret]
143 return [a.encode('latin-1') for a in ret]
140
144
141 else:
145 else:
142 import cStringIO
146 import cStringIO
143
147
144 bytechr = chr
148 bytechr = chr
149 iterbytestr = iter
145
150
146 def sysstr(s):
151 def sysstr(s):
147 return s
152 return s
148
153
149 # 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.
150 # 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
151 # indicates a bug.
156 # indicates a bug.
152 def fsencode(filename):
157 def fsencode(filename):
153 if isinstance(filename, str):
158 if isinstance(filename, str):
154 return filename
159 return filename
155 else:
160 else:
156 raise TypeError(
161 raise TypeError(
157 "expect str, not %s" % type(filename).__name__)
162 "expect str, not %s" % type(filename).__name__)
158
163
159 # 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
160 # 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.
161 def fsdecode(filename):
166 def fsdecode(filename):
162 return filename
167 return filename
163
168
164 def getoptb(args, shortlist, namelist):
169 def getoptb(args, shortlist, namelist):
165 return getopt.getopt(args, shortlist, namelist)
170 return getopt.getopt(args, shortlist, namelist)
166
171
167 def strkwargs(dic):
172 def strkwargs(dic):
168 return dic
173 return dic
169
174
170 def byteskwargs(dic):
175 def byteskwargs(dic):
171 return dic
176 return dic
172
177
173 osname = os.name
178 osname = os.name
174 ospathsep = os.pathsep
179 ospathsep = os.pathsep
175 ossep = os.sep
180 ossep = os.sep
176 osaltsep = os.altsep
181 osaltsep = os.altsep
177 stdin = sys.stdin
182 stdin = sys.stdin
178 stdout = sys.stdout
183 stdout = sys.stdout
179 stderr = sys.stderr
184 stderr = sys.stderr
180 if getattr(sys, 'argv', None) is not None:
185 if getattr(sys, 'argv', None) is not None:
181 sysargv = sys.argv
186 sysargv = sys.argv
182 sysplatform = sys.platform
187 sysplatform = sys.platform
183 getcwd = os.getcwd
188 getcwd = os.getcwd
184 sysexecutable = sys.executable
189 sysexecutable = sys.executable
185 shlexsplit = shlex.split
190 shlexsplit = shlex.split
186 stringio = cStringIO.StringIO
191 stringio = cStringIO.StringIO
187
192
188 empty = _queue.Empty
193 empty = _queue.Empty
189 queue = _queue.Queue
194 queue = _queue.Queue
190
195
191 class _pycompatstub(object):
196 class _pycompatstub(object):
192 def __init__(self):
197 def __init__(self):
193 self._aliases = {}
198 self._aliases = {}
194
199
195 def _registeraliases(self, origin, items):
200 def _registeraliases(self, origin, items):
196 """Add items that will be populated at the first access"""
201 """Add items that will be populated at the first access"""
197 items = map(sysstr, items)
202 items = map(sysstr, items)
198 self._aliases.update(
203 self._aliases.update(
199 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
204 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
200 for item in items)
205 for item in items)
201
206
202 def __getattr__(self, name):
207 def __getattr__(self, name):
203 try:
208 try:
204 origin, item = self._aliases[name]
209 origin, item = self._aliases[name]
205 except KeyError:
210 except KeyError:
206 raise AttributeError(name)
211 raise AttributeError(name)
207 self.__dict__[name] = obj = getattr(origin, item)
212 self.__dict__[name] = obj = getattr(origin, item)
208 return obj
213 return obj
209
214
210 httpserver = _pycompatstub()
215 httpserver = _pycompatstub()
211 urlreq = _pycompatstub()
216 urlreq = _pycompatstub()
212 urlerr = _pycompatstub()
217 urlerr = _pycompatstub()
213 if not ispy3:
218 if not ispy3:
214 import BaseHTTPServer
219 import BaseHTTPServer
215 import CGIHTTPServer
220 import CGIHTTPServer
216 import SimpleHTTPServer
221 import SimpleHTTPServer
217 import urllib2
222 import urllib2
218 import urllib
223 import urllib
219 urlreq._registeraliases(urllib, (
224 urlreq._registeraliases(urllib, (
220 "addclosehook",
225 "addclosehook",
221 "addinfourl",
226 "addinfourl",
222 "ftpwrapper",
227 "ftpwrapper",
223 "pathname2url",
228 "pathname2url",
224 "quote",
229 "quote",
225 "splitattr",
230 "splitattr",
226 "splitpasswd",
231 "splitpasswd",
227 "splitport",
232 "splitport",
228 "splituser",
233 "splituser",
229 "unquote",
234 "unquote",
230 "url2pathname",
235 "url2pathname",
231 "urlencode",
236 "urlencode",
232 ))
237 ))
233 urlreq._registeraliases(urllib2, (
238 urlreq._registeraliases(urllib2, (
234 "AbstractHTTPHandler",
239 "AbstractHTTPHandler",
235 "BaseHandler",
240 "BaseHandler",
236 "build_opener",
241 "build_opener",
237 "FileHandler",
242 "FileHandler",
238 "FTPHandler",
243 "FTPHandler",
239 "HTTPBasicAuthHandler",
244 "HTTPBasicAuthHandler",
240 "HTTPDigestAuthHandler",
245 "HTTPDigestAuthHandler",
241 "HTTPHandler",
246 "HTTPHandler",
242 "HTTPPasswordMgrWithDefaultRealm",
247 "HTTPPasswordMgrWithDefaultRealm",
243 "HTTPSHandler",
248 "HTTPSHandler",
244 "install_opener",
249 "install_opener",
245 "ProxyHandler",
250 "ProxyHandler",
246 "Request",
251 "Request",
247 "urlopen",
252 "urlopen",
248 ))
253 ))
249 urlerr._registeraliases(urllib2, (
254 urlerr._registeraliases(urllib2, (
250 "HTTPError",
255 "HTTPError",
251 "URLError",
256 "URLError",
252 ))
257 ))
253 httpserver._registeraliases(BaseHTTPServer, (
258 httpserver._registeraliases(BaseHTTPServer, (
254 "HTTPServer",
259 "HTTPServer",
255 "BaseHTTPRequestHandler",
260 "BaseHTTPRequestHandler",
256 ))
261 ))
257 httpserver._registeraliases(SimpleHTTPServer, (
262 httpserver._registeraliases(SimpleHTTPServer, (
258 "SimpleHTTPRequestHandler",
263 "SimpleHTTPRequestHandler",
259 ))
264 ))
260 httpserver._registeraliases(CGIHTTPServer, (
265 httpserver._registeraliases(CGIHTTPServer, (
261 "CGIHTTPRequestHandler",
266 "CGIHTTPRequestHandler",
262 ))
267 ))
263
268
264 else:
269 else:
265 import urllib.request
270 import urllib.request
266 urlreq._registeraliases(urllib.request, (
271 urlreq._registeraliases(urllib.request, (
267 "AbstractHTTPHandler",
272 "AbstractHTTPHandler",
268 "addclosehook",
273 "addclosehook",
269 "addinfourl",
274 "addinfourl",
270 "BaseHandler",
275 "BaseHandler",
271 "build_opener",
276 "build_opener",
272 "FileHandler",
277 "FileHandler",
273 "FTPHandler",
278 "FTPHandler",
274 "ftpwrapper",
279 "ftpwrapper",
275 "HTTPHandler",
280 "HTTPHandler",
276 "HTTPSHandler",
281 "HTTPSHandler",
277 "install_opener",
282 "install_opener",
278 "pathname2url",
283 "pathname2url",
279 "HTTPBasicAuthHandler",
284 "HTTPBasicAuthHandler",
280 "HTTPDigestAuthHandler",
285 "HTTPDigestAuthHandler",
281 "HTTPPasswordMgrWithDefaultRealm",
286 "HTTPPasswordMgrWithDefaultRealm",
282 "ProxyHandler",
287 "ProxyHandler",
283 "quote",
288 "quote",
284 "Request",
289 "Request",
285 "splitattr",
290 "splitattr",
286 "splitpasswd",
291 "splitpasswd",
287 "splitport",
292 "splitport",
288 "splituser",
293 "splituser",
289 "unquote",
294 "unquote",
290 "url2pathname",
295 "url2pathname",
291 "urlopen",
296 "urlopen",
292 ))
297 ))
293 import urllib.error
298 import urllib.error
294 urlerr._registeraliases(urllib.error, (
299 urlerr._registeraliases(urllib.error, (
295 "HTTPError",
300 "HTTPError",
296 "URLError",
301 "URLError",
297 ))
302 ))
298 import http.server
303 import http.server
299 httpserver._registeraliases(http.server, (
304 httpserver._registeraliases(http.server, (
300 "HTTPServer",
305 "HTTPServer",
301 "BaseHTTPRequestHandler",
306 "BaseHTTPRequestHandler",
302 "SimpleHTTPRequestHandler",
307 "SimpleHTTPRequestHandler",
303 "CGIHTTPRequestHandler",
308 "CGIHTTPRequestHandler",
304 ))
309 ))
General Comments 0
You need to be logged in to leave comments. Login now