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