##// END OF EJS Templates
py3: document why os.fsencode() can be used to get back bytes argv...
Yuya Nishihara -
r30334:19d8e19f default
parent child Browse files
Show More
@@ -1,214 +1,222 b''
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 os
14 14 import sys
15 15
16 16 ispy3 = (sys.version_info[0] >= 3)
17 17
18 18 if not ispy3:
19 19 import cPickle as pickle
20 20 import cStringIO as io
21 21 import httplib
22 22 import Queue as _queue
23 23 import SocketServer as socketserver
24 24 import urlparse
25 25 urlunquote = urlparse.unquote
26 26 import xmlrpclib
27 27 else:
28 28 import http.client as httplib
29 29 import io
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 fsencode = os.fsencode
41 41 fsdecode = os.fsdecode
42 42 # A bytes version of os.name.
43 43 osname = os.name.encode('ascii')
44 44 ospathsep = os.pathsep.encode('ascii')
45 45 ossep = os.sep.encode('ascii')
46
47 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
48 # we can use os.fsencode() to get back bytes argv.
49 #
50 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
51 #
52 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
53 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
46 54 sysargv = list(map(os.fsencode, sys.argv))
47 55
48 56 def sysstr(s):
49 57 """Return a keyword str to be passed to Python functions such as
50 58 getattr() and str.encode()
51 59
52 60 This never raises UnicodeDecodeError. Non-ascii characters are
53 61 considered invalid and mapped to arbitrary but unique code points
54 62 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
55 63 """
56 64 if isinstance(s, builtins.str):
57 65 return s
58 66 return s.decode(u'latin-1')
59 67
60 68 def _wrapattrfunc(f):
61 69 @functools.wraps(f)
62 70 def w(object, name, *args):
63 71 return f(object, sysstr(name), *args)
64 72 return w
65 73
66 74 # these wrappers are automagically imported by hgloader
67 75 delattr = _wrapattrfunc(builtins.delattr)
68 76 getattr = _wrapattrfunc(builtins.getattr)
69 77 hasattr = _wrapattrfunc(builtins.hasattr)
70 78 setattr = _wrapattrfunc(builtins.setattr)
71 79 xrange = builtins.range
72 80
73 81 else:
74 82 def sysstr(s):
75 83 return s
76 84
77 85 # Partial backport from os.py in Python 3, which only accepts bytes.
78 86 # In Python 2, our paths should only ever be bytes, a unicode path
79 87 # indicates a bug.
80 88 def fsencode(filename):
81 89 if isinstance(filename, str):
82 90 return filename
83 91 else:
84 92 raise TypeError(
85 93 "expect str, not %s" % type(filename).__name__)
86 94
87 95 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
88 96 # better not to touch Python 2 part as it's already working fine.
89 97 def fsdecode(filename):
90 98 return filename
91 99
92 100 osname = os.name
93 101 ospathsep = os.pathsep
94 102 ossep = os.sep
95 103 sysargv = sys.argv
96 104
97 105 stringio = io.StringIO
98 106 empty = _queue.Empty
99 107 queue = _queue.Queue
100 108
101 109 class _pycompatstub(object):
102 110 def __init__(self):
103 111 self._aliases = {}
104 112
105 113 def _registeraliases(self, origin, items):
106 114 """Add items that will be populated at the first access"""
107 115 items = map(sysstr, items)
108 116 self._aliases.update(
109 117 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
110 118 for item in items)
111 119
112 120 def __getattr__(self, name):
113 121 try:
114 122 origin, item = self._aliases[name]
115 123 except KeyError:
116 124 raise AttributeError(name)
117 125 self.__dict__[name] = obj = getattr(origin, item)
118 126 return obj
119 127
120 128 httpserver = _pycompatstub()
121 129 urlreq = _pycompatstub()
122 130 urlerr = _pycompatstub()
123 131 if not ispy3:
124 132 import BaseHTTPServer
125 133 import CGIHTTPServer
126 134 import SimpleHTTPServer
127 135 import urllib2
128 136 import urllib
129 137 urlreq._registeraliases(urllib, (
130 138 "addclosehook",
131 139 "addinfourl",
132 140 "ftpwrapper",
133 141 "pathname2url",
134 142 "quote",
135 143 "splitattr",
136 144 "splitpasswd",
137 145 "splitport",
138 146 "splituser",
139 147 "unquote",
140 148 "url2pathname",
141 149 "urlencode",
142 150 ))
143 151 urlreq._registeraliases(urllib2, (
144 152 "AbstractHTTPHandler",
145 153 "BaseHandler",
146 154 "build_opener",
147 155 "FileHandler",
148 156 "FTPHandler",
149 157 "HTTPBasicAuthHandler",
150 158 "HTTPDigestAuthHandler",
151 159 "HTTPHandler",
152 160 "HTTPPasswordMgrWithDefaultRealm",
153 161 "HTTPSHandler",
154 162 "install_opener",
155 163 "ProxyHandler",
156 164 "Request",
157 165 "urlopen",
158 166 ))
159 167 urlerr._registeraliases(urllib2, (
160 168 "HTTPError",
161 169 "URLError",
162 170 ))
163 171 httpserver._registeraliases(BaseHTTPServer, (
164 172 "HTTPServer",
165 173 "BaseHTTPRequestHandler",
166 174 ))
167 175 httpserver._registeraliases(SimpleHTTPServer, (
168 176 "SimpleHTTPRequestHandler",
169 177 ))
170 178 httpserver._registeraliases(CGIHTTPServer, (
171 179 "CGIHTTPRequestHandler",
172 180 ))
173 181
174 182 else:
175 183 import urllib.request
176 184 urlreq._registeraliases(urllib.request, (
177 185 "AbstractHTTPHandler",
178 186 "addclosehook",
179 187 "addinfourl",
180 188 "BaseHandler",
181 189 "build_opener",
182 190 "FileHandler",
183 191 "FTPHandler",
184 192 "ftpwrapper",
185 193 "HTTPHandler",
186 194 "HTTPSHandler",
187 195 "install_opener",
188 196 "pathname2url",
189 197 "HTTPBasicAuthHandler",
190 198 "HTTPDigestAuthHandler",
191 199 "HTTPPasswordMgrWithDefaultRealm",
192 200 "ProxyHandler",
193 201 "quote",
194 202 "Request",
195 203 "splitattr",
196 204 "splitpasswd",
197 205 "splitport",
198 206 "splituser",
199 207 "unquote",
200 208 "url2pathname",
201 209 "urlopen",
202 210 ))
203 211 import urllib.error
204 212 urlerr._registeraliases(urllib.error, (
205 213 "HTTPError",
206 214 "URLError",
207 215 ))
208 216 import http.server
209 217 httpserver._registeraliases(http.server, (
210 218 "HTTPServer",
211 219 "BaseHTTPRequestHandler",
212 220 "SimpleHTTPRequestHandler",
213 221 "CGIHTTPRequestHandler",
214 222 ))
General Comments 0
You need to be logged in to leave comments. Login now