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