##// END OF EJS Templates
py3: add pycompat.unicode and add it to importer...
Pulkit Goyal -
r31843:526e4597 default
parent child Browse files
Show More
@@ -1,402 +1,403 b''
1 # __init__.py - Startup and module loading logic for Mercurial.
1 # __init__.py - Startup and module loading logic for Mercurial.
2 #
2 #
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import imp
10 import imp
11 import os
11 import os
12 import sys
12 import sys
13 import zipimport
13 import zipimport
14
14
15 from . import (
15 from . import (
16 policy
16 policy
17 )
17 )
18
18
19 __all__ = []
19 __all__ = []
20
20
21 modulepolicy = policy.policy
21 modulepolicy = policy.policy
22
22
23 # Modules that have both Python and C implementations. See also the
23 # Modules that have both Python and C implementations. See also the
24 # set of .py files under mercurial/pure/.
24 # set of .py files under mercurial/pure/.
25 _dualmodules = set([
25 _dualmodules = set([
26 'mercurial.base85',
26 'mercurial.base85',
27 'mercurial.bdiff',
27 'mercurial.bdiff',
28 'mercurial.diffhelpers',
28 'mercurial.diffhelpers',
29 'mercurial.mpatch',
29 'mercurial.mpatch',
30 'mercurial.osutil',
30 'mercurial.osutil',
31 'mercurial.parsers',
31 'mercurial.parsers',
32 ])
32 ])
33
33
34 class hgimporter(object):
34 class hgimporter(object):
35 """Object that conforms to import hook interface defined in PEP-302."""
35 """Object that conforms to import hook interface defined in PEP-302."""
36 def find_module(self, name, path=None):
36 def find_module(self, name, path=None):
37 # We only care about modules that have both C and pure implementations.
37 # We only care about modules that have both C and pure implementations.
38 if name in _dualmodules:
38 if name in _dualmodules:
39 return self
39 return self
40 return None
40 return None
41
41
42 def load_module(self, name):
42 def load_module(self, name):
43 mod = sys.modules.get(name, None)
43 mod = sys.modules.get(name, None)
44 if mod:
44 if mod:
45 return mod
45 return mod
46
46
47 mercurial = sys.modules['mercurial']
47 mercurial = sys.modules['mercurial']
48
48
49 # The zip importer behaves sufficiently differently from the default
49 # The zip importer behaves sufficiently differently from the default
50 # importer to warrant its own code path.
50 # importer to warrant its own code path.
51 loader = getattr(mercurial, '__loader__', None)
51 loader = getattr(mercurial, '__loader__', None)
52 if isinstance(loader, zipimport.zipimporter):
52 if isinstance(loader, zipimport.zipimporter):
53 def ziploader(*paths):
53 def ziploader(*paths):
54 """Obtain a zipimporter for a directory under the main zip."""
54 """Obtain a zipimporter for a directory under the main zip."""
55 path = os.path.join(loader.archive, *paths)
55 path = os.path.join(loader.archive, *paths)
56 zl = sys.path_importer_cache.get(path)
56 zl = sys.path_importer_cache.get(path)
57 if not zl:
57 if not zl:
58 zl = zipimport.zipimporter(path)
58 zl = zipimport.zipimporter(path)
59 return zl
59 return zl
60
60
61 try:
61 try:
62 if modulepolicy in policy.policynoc:
62 if modulepolicy in policy.policynoc:
63 raise ImportError()
63 raise ImportError()
64
64
65 zl = ziploader('mercurial')
65 zl = ziploader('mercurial')
66 mod = zl.load_module(name)
66 mod = zl.load_module(name)
67 # Unlike imp, ziploader doesn't expose module metadata that
67 # Unlike imp, ziploader doesn't expose module metadata that
68 # indicates the type of module. So just assume what we found
68 # indicates the type of module. So just assume what we found
69 # is OK (even though it could be a pure Python module).
69 # is OK (even though it could be a pure Python module).
70 except ImportError:
70 except ImportError:
71 if modulepolicy == b'c':
71 if modulepolicy == b'c':
72 raise
72 raise
73 zl = ziploader('mercurial', 'pure')
73 zl = ziploader('mercurial', 'pure')
74 mod = zl.load_module(name)
74 mod = zl.load_module(name)
75
75
76 sys.modules[name] = mod
76 sys.modules[name] = mod
77 return mod
77 return mod
78
78
79 # Unlike the default importer which searches special locations and
79 # Unlike the default importer which searches special locations and
80 # sys.path, we only look in the directory where "mercurial" was
80 # sys.path, we only look in the directory where "mercurial" was
81 # imported from.
81 # imported from.
82
82
83 # imp.find_module doesn't support submodules (modules with ".").
83 # imp.find_module doesn't support submodules (modules with ".").
84 # Instead you have to pass the parent package's __path__ attribute
84 # Instead you have to pass the parent package's __path__ attribute
85 # as the path argument.
85 # as the path argument.
86 stem = name.split('.')[-1]
86 stem = name.split('.')[-1]
87
87
88 try:
88 try:
89 if modulepolicy in policy.policynoc:
89 if modulepolicy in policy.policynoc:
90 raise ImportError()
90 raise ImportError()
91
91
92 modinfo = imp.find_module(stem, mercurial.__path__)
92 modinfo = imp.find_module(stem, mercurial.__path__)
93
93
94 # The Mercurial installer used to copy files from
94 # The Mercurial installer used to copy files from
95 # mercurial/pure/*.py to mercurial/*.py. Therefore, it's possible
95 # mercurial/pure/*.py to mercurial/*.py. Therefore, it's possible
96 # for some installations to have .py files under mercurial/*.
96 # for some installations to have .py files under mercurial/*.
97 # Loading Python modules when we expected C versions could result
97 # Loading Python modules when we expected C versions could result
98 # in a) poor performance b) loading a version from a previous
98 # in a) poor performance b) loading a version from a previous
99 # Mercurial version, potentially leading to incompatibility. Either
99 # Mercurial version, potentially leading to incompatibility. Either
100 # scenario is bad. So we verify that modules loaded from
100 # scenario is bad. So we verify that modules loaded from
101 # mercurial/* are C extensions. If the current policy allows the
101 # mercurial/* are C extensions. If the current policy allows the
102 # loading of .py modules, the module will be re-imported from
102 # loading of .py modules, the module will be re-imported from
103 # mercurial/pure/* below.
103 # mercurial/pure/* below.
104 if modinfo[2][2] != imp.C_EXTENSION:
104 if modinfo[2][2] != imp.C_EXTENSION:
105 raise ImportError('.py version of %s found where C '
105 raise ImportError('.py version of %s found where C '
106 'version should exist' % name)
106 'version should exist' % name)
107
107
108 except ImportError:
108 except ImportError:
109 if modulepolicy == b'c':
109 if modulepolicy == b'c':
110 raise
110 raise
111
111
112 # Could not load the C extension and pure Python is allowed. So
112 # Could not load the C extension and pure Python is allowed. So
113 # try to load them.
113 # try to load them.
114 from . import pure
114 from . import pure
115 modinfo = imp.find_module(stem, pure.__path__)
115 modinfo = imp.find_module(stem, pure.__path__)
116 if not modinfo:
116 if not modinfo:
117 raise ImportError('could not find mercurial module %s' %
117 raise ImportError('could not find mercurial module %s' %
118 name)
118 name)
119
119
120 mod = imp.load_module(name, *modinfo)
120 mod = imp.load_module(name, *modinfo)
121 sys.modules[name] = mod
121 sys.modules[name] = mod
122 return mod
122 return mod
123
123
124 # Python 3 uses a custom module loader that transforms source code between
124 # Python 3 uses a custom module loader that transforms source code between
125 # source file reading and compilation. This is done by registering a custom
125 # source file reading and compilation. This is done by registering a custom
126 # finder that changes the spec for Mercurial modules to use a custom loader.
126 # finder that changes the spec for Mercurial modules to use a custom loader.
127 if sys.version_info[0] >= 3:
127 if sys.version_info[0] >= 3:
128 from . import pure
128 from . import pure
129 import importlib
129 import importlib
130 import io
130 import io
131 import token
131 import token
132 import tokenize
132 import tokenize
133
133
134 class hgpathentryfinder(importlib.abc.MetaPathFinder):
134 class hgpathentryfinder(importlib.abc.MetaPathFinder):
135 """A sys.meta_path finder that uses a custom module loader."""
135 """A sys.meta_path finder that uses a custom module loader."""
136 def find_spec(self, fullname, path, target=None):
136 def find_spec(self, fullname, path, target=None):
137 # Only handle Mercurial-related modules.
137 # Only handle Mercurial-related modules.
138 if not fullname.startswith(('mercurial.', 'hgext.', 'hgext3rd.')):
138 if not fullname.startswith(('mercurial.', 'hgext.', 'hgext3rd.')):
139 return None
139 return None
140 # zstd is already dual-version clean, don't try and mangle it
140 # zstd is already dual-version clean, don't try and mangle it
141 if fullname.startswith('mercurial.zstd'):
141 if fullname.startswith('mercurial.zstd'):
142 return None
142 return None
143
143
144 # This assumes Python 3 doesn't support loading C modules.
144 # This assumes Python 3 doesn't support loading C modules.
145 if fullname in _dualmodules:
145 if fullname in _dualmodules:
146 stem = fullname.split('.')[-1]
146 stem = fullname.split('.')[-1]
147 fullname = 'mercurial.pure.%s' % stem
147 fullname = 'mercurial.pure.%s' % stem
148 target = pure
148 target = pure
149 assert len(path) == 1
149 assert len(path) == 1
150 path = [os.path.join(path[0], 'pure')]
150 path = [os.path.join(path[0], 'pure')]
151
151
152 # Try to find the module using other registered finders.
152 # Try to find the module using other registered finders.
153 spec = None
153 spec = None
154 for finder in sys.meta_path:
154 for finder in sys.meta_path:
155 if finder == self:
155 if finder == self:
156 continue
156 continue
157
157
158 spec = finder.find_spec(fullname, path, target=target)
158 spec = finder.find_spec(fullname, path, target=target)
159 if spec:
159 if spec:
160 break
160 break
161
161
162 # This is a Mercurial-related module but we couldn't find it
162 # This is a Mercurial-related module but we couldn't find it
163 # using the previously-registered finders. This likely means
163 # using the previously-registered finders. This likely means
164 # the module doesn't exist.
164 # the module doesn't exist.
165 if not spec:
165 if not spec:
166 return None
166 return None
167
167
168 if fullname.startswith('mercurial.pure.'):
168 if fullname.startswith('mercurial.pure.'):
169 spec.name = spec.name.replace('.pure.', '.')
169 spec.name = spec.name.replace('.pure.', '.')
170
170
171 # TODO need to support loaders from alternate specs, like zip
171 # TODO need to support loaders from alternate specs, like zip
172 # loaders.
172 # loaders.
173 spec.loader = hgloader(spec.name, spec.origin)
173 spec.loader = hgloader(spec.name, spec.origin)
174 return spec
174 return spec
175
175
176 def replacetokens(tokens, fullname):
176 def replacetokens(tokens, fullname):
177 """Transform a stream of tokens from raw to Python 3.
177 """Transform a stream of tokens from raw to Python 3.
178
178
179 It is called by the custom module loading machinery to rewrite
179 It is called by the custom module loading machinery to rewrite
180 source/tokens between source decoding and compilation.
180 source/tokens between source decoding and compilation.
181
181
182 Returns a generator of possibly rewritten tokens.
182 Returns a generator of possibly rewritten tokens.
183
183
184 The input token list may be mutated as part of processing. However,
184 The input token list may be mutated as part of processing. However,
185 its changes do not necessarily match the output token stream.
185 its changes do not necessarily match the output token stream.
186
186
187 REMEMBER TO CHANGE ``BYTECODEHEADER`` WHEN CHANGING THIS FUNCTION
187 REMEMBER TO CHANGE ``BYTECODEHEADER`` WHEN CHANGING THIS FUNCTION
188 OR CACHED FILES WON'T GET INVALIDATED PROPERLY.
188 OR CACHED FILES WON'T GET INVALIDATED PROPERLY.
189 """
189 """
190 futureimpline = False
190 futureimpline = False
191
191
192 # The following utility functions access the tokens list and i index of
192 # The following utility functions access the tokens list and i index of
193 # the for i, t enumerate(tokens) loop below
193 # the for i, t enumerate(tokens) loop below
194 def _isop(j, *o):
194 def _isop(j, *o):
195 """Assert that tokens[j] is an OP with one of the given values"""
195 """Assert that tokens[j] is an OP with one of the given values"""
196 try:
196 try:
197 return tokens[j].type == token.OP and tokens[j].string in o
197 return tokens[j].type == token.OP and tokens[j].string in o
198 except IndexError:
198 except IndexError:
199 return False
199 return False
200
200
201 def _findargnofcall(n):
201 def _findargnofcall(n):
202 """Find arg n of a call expression (start at 0)
202 """Find arg n of a call expression (start at 0)
203
203
204 Returns index of the first token of that argument, or None if
204 Returns index of the first token of that argument, or None if
205 there is not that many arguments.
205 there is not that many arguments.
206
206
207 Assumes that token[i + 1] is '('.
207 Assumes that token[i + 1] is '('.
208
208
209 """
209 """
210 nested = 0
210 nested = 0
211 for j in range(i + 2, len(tokens)):
211 for j in range(i + 2, len(tokens)):
212 if _isop(j, ')', ']', '}'):
212 if _isop(j, ')', ']', '}'):
213 # end of call, tuple, subscription or dict / set
213 # end of call, tuple, subscription or dict / set
214 nested -= 1
214 nested -= 1
215 if nested < 0:
215 if nested < 0:
216 return None
216 return None
217 elif n == 0:
217 elif n == 0:
218 # this is the starting position of arg
218 # this is the starting position of arg
219 return j
219 return j
220 elif _isop(j, '(', '[', '{'):
220 elif _isop(j, '(', '[', '{'):
221 nested += 1
221 nested += 1
222 elif _isop(j, ',') and nested == 0:
222 elif _isop(j, ',') and nested == 0:
223 n -= 1
223 n -= 1
224
224
225 return None
225 return None
226
226
227 def _ensureunicode(j):
227 def _ensureunicode(j):
228 """Make sure the token at j is a unicode string
228 """Make sure the token at j is a unicode string
229
229
230 This rewrites a string token to include the unicode literal prefix
230 This rewrites a string token to include the unicode literal prefix
231 so the string transformer won't add the byte prefix.
231 so the string transformer won't add the byte prefix.
232
232
233 Ignores tokens that are not strings. Assumes bounds checking has
233 Ignores tokens that are not strings. Assumes bounds checking has
234 already been done.
234 already been done.
235
235
236 """
236 """
237 st = tokens[j]
237 st = tokens[j]
238 if st.type == token.STRING and st.string.startswith(("'", '"')):
238 if st.type == token.STRING and st.string.startswith(("'", '"')):
239 tokens[j] = st._replace(string='u%s' % st.string)
239 tokens[j] = st._replace(string='u%s' % st.string)
240
240
241 for i, t in enumerate(tokens):
241 for i, t in enumerate(tokens):
242 # Convert most string literals to byte literals. String literals
242 # Convert most string literals to byte literals. String literals
243 # in Python 2 are bytes. String literals in Python 3 are unicode.
243 # in Python 2 are bytes. String literals in Python 3 are unicode.
244 # Most strings in Mercurial are bytes and unicode strings are rare.
244 # Most strings in Mercurial are bytes and unicode strings are rare.
245 # Rather than rewrite all string literals to use ``b''`` to indicate
245 # Rather than rewrite all string literals to use ``b''`` to indicate
246 # byte strings, we apply this token transformer to insert the ``b``
246 # byte strings, we apply this token transformer to insert the ``b``
247 # prefix nearly everywhere.
247 # prefix nearly everywhere.
248 if t.type == token.STRING:
248 if t.type == token.STRING:
249 s = t.string
249 s = t.string
250
250
251 # Preserve docstrings as string literals. This is inconsistent
251 # Preserve docstrings as string literals. This is inconsistent
252 # with regular unprefixed strings. However, the
252 # with regular unprefixed strings. However, the
253 # "from __future__" parsing (which allows a module docstring to
253 # "from __future__" parsing (which allows a module docstring to
254 # exist before it) doesn't properly handle the docstring if it
254 # exist before it) doesn't properly handle the docstring if it
255 # is b''' prefixed, leading to a SyntaxError. We leave all
255 # is b''' prefixed, leading to a SyntaxError. We leave all
256 # docstrings as unprefixed to avoid this. This means Mercurial
256 # docstrings as unprefixed to avoid this. This means Mercurial
257 # components touching docstrings need to handle unicode,
257 # components touching docstrings need to handle unicode,
258 # unfortunately.
258 # unfortunately.
259 if s[0:3] in ("'''", '"""'):
259 if s[0:3] in ("'''", '"""'):
260 yield t
260 yield t
261 continue
261 continue
262
262
263 # If the first character isn't a quote, it is likely a string
263 # If the first character isn't a quote, it is likely a string
264 # prefixing character (such as 'b', 'u', or 'r'. Ignore.
264 # prefixing character (such as 'b', 'u', or 'r'. Ignore.
265 if s[0] not in ("'", '"'):
265 if s[0] not in ("'", '"'):
266 yield t
266 yield t
267 continue
267 continue
268
268
269 # String literal. Prefix to make a b'' string.
269 # String literal. Prefix to make a b'' string.
270 yield t._replace(string='b%s' % t.string)
270 yield t._replace(string='b%s' % t.string)
271 continue
271 continue
272
272
273 # Insert compatibility imports at "from __future__ import" line.
273 # Insert compatibility imports at "from __future__ import" line.
274 # No '\n' should be added to preserve line numbers.
274 # No '\n' should be added to preserve line numbers.
275 if (t.type == token.NAME and t.string == 'import' and
275 if (t.type == token.NAME and t.string == 'import' and
276 all(u.type == token.NAME for u in tokens[i - 2:i]) and
276 all(u.type == token.NAME for u in tokens[i - 2:i]) and
277 [u.string for u in tokens[i - 2:i]] == ['from', '__future__']):
277 [u.string for u in tokens[i - 2:i]] == ['from', '__future__']):
278 futureimpline = True
278 futureimpline = True
279 if t.type == token.NEWLINE and futureimpline:
279 if t.type == token.NEWLINE and futureimpline:
280 futureimpline = False
280 futureimpline = False
281 if fullname == 'mercurial.pycompat':
281 if fullname == 'mercurial.pycompat':
282 yield t
282 yield t
283 continue
283 continue
284 r, c = t.start
284 r, c = t.start
285 l = (b'; from mercurial.pycompat import '
285 l = (b'; from mercurial.pycompat import '
286 b'delattr, getattr, hasattr, setattr, xrange, open\n')
286 b'delattr, getattr, hasattr, setattr, xrange, '
287 b'open, unicode\n')
287 for u in tokenize.tokenize(io.BytesIO(l).readline):
288 for u in tokenize.tokenize(io.BytesIO(l).readline):
288 if u.type in (tokenize.ENCODING, token.ENDMARKER):
289 if u.type in (tokenize.ENCODING, token.ENDMARKER):
289 continue
290 continue
290 yield u._replace(
291 yield u._replace(
291 start=(r, c + u.start[1]), end=(r, c + u.end[1]))
292 start=(r, c + u.start[1]), end=(r, c + u.end[1]))
292 continue
293 continue
293
294
294 # This looks like a function call.
295 # This looks like a function call.
295 if t.type == token.NAME and _isop(i + 1, '('):
296 if t.type == token.NAME and _isop(i + 1, '('):
296 fn = t.string
297 fn = t.string
297
298
298 # *attr() builtins don't accept byte strings to 2nd argument.
299 # *attr() builtins don't accept byte strings to 2nd argument.
299 if (fn in ('getattr', 'setattr', 'hasattr', 'safehasattr') and
300 if (fn in ('getattr', 'setattr', 'hasattr', 'safehasattr') and
300 not _isop(i - 1, '.')):
301 not _isop(i - 1, '.')):
301 arg1idx = _findargnofcall(1)
302 arg1idx = _findargnofcall(1)
302 if arg1idx is not None:
303 if arg1idx is not None:
303 _ensureunicode(arg1idx)
304 _ensureunicode(arg1idx)
304
305
305 # .encode() and .decode() on str/bytes/unicode don't accept
306 # .encode() and .decode() on str/bytes/unicode don't accept
306 # byte strings on Python 3.
307 # byte strings on Python 3.
307 elif fn in ('encode', 'decode') and _isop(i - 1, '.'):
308 elif fn in ('encode', 'decode') and _isop(i - 1, '.'):
308 for argn in range(2):
309 for argn in range(2):
309 argidx = _findargnofcall(argn)
310 argidx = _findargnofcall(argn)
310 if argidx is not None:
311 if argidx is not None:
311 _ensureunicode(argidx)
312 _ensureunicode(argidx)
312
313
313 # It changes iteritems/values to items/values as they are not
314 # It changes iteritems/values to items/values as they are not
314 # present in Python 3 world.
315 # present in Python 3 world.
315 elif fn in ('iteritems', 'itervalues'):
316 elif fn in ('iteritems', 'itervalues'):
316 yield t._replace(string=fn[4:])
317 yield t._replace(string=fn[4:])
317 continue
318 continue
318
319
319 # Emit unmodified token.
320 # Emit unmodified token.
320 yield t
321 yield t
321
322
322 # Header to add to bytecode files. This MUST be changed when
323 # Header to add to bytecode files. This MUST be changed when
323 # ``replacetoken`` or any mechanism that changes semantics of module
324 # ``replacetoken`` or any mechanism that changes semantics of module
324 # loading is changed. Otherwise cached bytecode may get loaded without
325 # loading is changed. Otherwise cached bytecode may get loaded without
325 # the new transformation mechanisms applied.
326 # the new transformation mechanisms applied.
326 BYTECODEHEADER = b'HG\x00\x09'
327 BYTECODEHEADER = b'HG\x00\x0a'
327
328
328 class hgloader(importlib.machinery.SourceFileLoader):
329 class hgloader(importlib.machinery.SourceFileLoader):
329 """Custom module loader that transforms source code.
330 """Custom module loader that transforms source code.
330
331
331 When the source code is converted to a code object, we transform
332 When the source code is converted to a code object, we transform
332 certain patterns to be Python 3 compatible. This allows us to write code
333 certain patterns to be Python 3 compatible. This allows us to write code
333 that is natively Python 2 and compatible with Python 3 without
334 that is natively Python 2 and compatible with Python 3 without
334 making the code excessively ugly.
335 making the code excessively ugly.
335
336
336 We do this by transforming the token stream between parse and compile.
337 We do this by transforming the token stream between parse and compile.
337
338
338 Implementing transformations invalidates caching assumptions made
339 Implementing transformations invalidates caching assumptions made
339 by the built-in importer. The built-in importer stores a header on
340 by the built-in importer. The built-in importer stores a header on
340 saved bytecode files indicating the Python/bytecode version. If the
341 saved bytecode files indicating the Python/bytecode version. If the
341 version changes, the cached bytecode is ignored. The Mercurial
342 version changes, the cached bytecode is ignored. The Mercurial
342 transformations could change at any time. This means we need to check
343 transformations could change at any time. This means we need to check
343 that cached bytecode was generated with the current transformation
344 that cached bytecode was generated with the current transformation
344 code or there could be a mismatch between cached bytecode and what
345 code or there could be a mismatch between cached bytecode and what
345 would be generated from this class.
346 would be generated from this class.
346
347
347 We supplement the bytecode caching layer by wrapping ``get_data``
348 We supplement the bytecode caching layer by wrapping ``get_data``
348 and ``set_data``. These functions are called when the
349 and ``set_data``. These functions are called when the
349 ``SourceFileLoader`` retrieves and saves bytecode cache files,
350 ``SourceFileLoader`` retrieves and saves bytecode cache files,
350 respectively. We simply add an additional header on the file. As
351 respectively. We simply add an additional header on the file. As
351 long as the version in this file is changed when semantics change,
352 long as the version in this file is changed when semantics change,
352 cached bytecode should be invalidated when transformations change.
353 cached bytecode should be invalidated when transformations change.
353
354
354 The added header has the form ``HG<VERSION>``. That is a literal
355 The added header has the form ``HG<VERSION>``. That is a literal
355 ``HG`` with 2 binary bytes indicating the transformation version.
356 ``HG`` with 2 binary bytes indicating the transformation version.
356 """
357 """
357 def get_data(self, path):
358 def get_data(self, path):
358 data = super(hgloader, self).get_data(path)
359 data = super(hgloader, self).get_data(path)
359
360
360 if not path.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
361 if not path.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
361 return data
362 return data
362
363
363 # There should be a header indicating the Mercurial transformation
364 # There should be a header indicating the Mercurial transformation
364 # version. If it doesn't exist or doesn't match the current version,
365 # version. If it doesn't exist or doesn't match the current version,
365 # we raise an OSError because that is what
366 # we raise an OSError because that is what
366 # ``SourceFileLoader.get_code()`` expects when loading bytecode
367 # ``SourceFileLoader.get_code()`` expects when loading bytecode
367 # paths to indicate the cached file is "bad."
368 # paths to indicate the cached file is "bad."
368 if data[0:2] != b'HG':
369 if data[0:2] != b'HG':
369 raise OSError('no hg header')
370 raise OSError('no hg header')
370 if data[0:4] != BYTECODEHEADER:
371 if data[0:4] != BYTECODEHEADER:
371 raise OSError('hg header version mismatch')
372 raise OSError('hg header version mismatch')
372
373
373 return data[4:]
374 return data[4:]
374
375
375 def set_data(self, path, data, *args, **kwargs):
376 def set_data(self, path, data, *args, **kwargs):
376 if path.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
377 if path.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
377 data = BYTECODEHEADER + data
378 data = BYTECODEHEADER + data
378
379
379 return super(hgloader, self).set_data(path, data, *args, **kwargs)
380 return super(hgloader, self).set_data(path, data, *args, **kwargs)
380
381
381 def source_to_code(self, data, path):
382 def source_to_code(self, data, path):
382 """Perform token transformation before compilation."""
383 """Perform token transformation before compilation."""
383 buf = io.BytesIO(data)
384 buf = io.BytesIO(data)
384 tokens = tokenize.tokenize(buf.readline)
385 tokens = tokenize.tokenize(buf.readline)
385 data = tokenize.untokenize(replacetokens(list(tokens), self.name))
386 data = tokenize.untokenize(replacetokens(list(tokens), self.name))
386 # Python's built-in importer strips frames from exceptions raised
387 # Python's built-in importer strips frames from exceptions raised
387 # for this code. Unfortunately, that mechanism isn't extensible
388 # for this code. Unfortunately, that mechanism isn't extensible
388 # and our frame will be blamed for the import failure. There
389 # and our frame will be blamed for the import failure. There
389 # are extremely hacky ways to do frame stripping. We haven't
390 # are extremely hacky ways to do frame stripping. We haven't
390 # implemented them because they are very ugly.
391 # implemented them because they are very ugly.
391 return super(hgloader, self).source_to_code(data, path)
392 return super(hgloader, self).source_to_code(data, path)
392
393
393 # We automagically register our custom importer as a side-effect of loading.
394 # We automagically register our custom importer as a side-effect of loading.
394 # This is necessary to ensure that any entry points are able to import
395 # This is necessary to ensure that any entry points are able to import
395 # mercurial.* modules without having to perform this registration themselves.
396 # mercurial.* modules without having to perform this registration themselves.
396 if sys.version_info[0] >= 3:
397 if sys.version_info[0] >= 3:
397 _importercls = hgpathentryfinder
398 _importercls = hgpathentryfinder
398 else:
399 else:
399 _importercls = hgimporter
400 _importercls = hgimporter
400 if not any(isinstance(x, _importercls) for x in sys.meta_path):
401 if not any(isinstance(x, _importercls) for x in sys.meta_path):
401 # meta_path is used before any implicit finders and before sys.path.
402 # meta_path is used before any implicit finders and before sys.path.
402 sys.meta_path.insert(0, _importercls())
403 sys.meta_path.insert(0, _importercls())
@@ -1,409 +1,410 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 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 xmlrpclib
25 import xmlrpclib
26 else:
26 else:
27 import http.client as httplib
27 import http.client as httplib
28 import pickle
28 import pickle
29 import queue as _queue
29 import queue as _queue
30 import socketserver
30 import socketserver
31 import xmlrpc.client as xmlrpclib
31 import xmlrpc.client as xmlrpclib
32
32
33 def identity(a):
33 def identity(a):
34 return a
34 return a
35
35
36 if ispy3:
36 if ispy3:
37 import builtins
37 import builtins
38 import functools
38 import functools
39 import io
39 import io
40 import struct
40 import struct
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 oslinesep = os.linesep.encode('ascii')
45 oslinesep = os.linesep.encode('ascii')
46 osname = os.name.encode('ascii')
46 osname = os.name.encode('ascii')
47 ospathsep = os.pathsep.encode('ascii')
47 ospathsep = os.pathsep.encode('ascii')
48 ossep = os.sep.encode('ascii')
48 ossep = os.sep.encode('ascii')
49 osaltsep = os.altsep
49 osaltsep = os.altsep
50 if osaltsep:
50 if osaltsep:
51 osaltsep = osaltsep.encode('ascii')
51 osaltsep = osaltsep.encode('ascii')
52 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
52 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
53 # returns bytes.
53 # returns bytes.
54 getcwd = os.getcwdb
54 getcwd = os.getcwdb
55 sysplatform = sys.platform.encode('ascii')
55 sysplatform = sys.platform.encode('ascii')
56 sysexecutable = sys.executable
56 sysexecutable = sys.executable
57 if sysexecutable:
57 if sysexecutable:
58 sysexecutable = os.fsencode(sysexecutable)
58 sysexecutable = os.fsencode(sysexecutable)
59 stringio = io.BytesIO
59 stringio = io.BytesIO
60 maplist = lambda *args: list(map(*args))
60 maplist = lambda *args: list(map(*args))
61
61
62 # TODO: .buffer might not exist if std streams were replaced; we'll need
62 # TODO: .buffer might not exist if std streams were replaced; we'll need
63 # a silly wrapper to make a bytes stream backed by a unicode one.
63 # a silly wrapper to make a bytes stream backed by a unicode one.
64 stdin = sys.stdin.buffer
64 stdin = sys.stdin.buffer
65 stdout = sys.stdout.buffer
65 stdout = sys.stdout.buffer
66 stderr = sys.stderr.buffer
66 stderr = sys.stderr.buffer
67
67
68 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
68 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
69 # we can use os.fsencode() to get back bytes argv.
69 # we can use os.fsencode() to get back bytes argv.
70 #
70 #
71 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
71 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
72 #
72 #
73 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
73 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
74 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
74 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
75 if getattr(sys, 'argv', None) is not None:
75 if getattr(sys, 'argv', None) is not None:
76 sysargv = list(map(os.fsencode, sys.argv))
76 sysargv = list(map(os.fsencode, sys.argv))
77
77
78 bytechr = struct.Struct('>B').pack
78 bytechr = struct.Struct('>B').pack
79
79
80 class bytestr(bytes):
80 class bytestr(bytes):
81 """A bytes which mostly acts as a Python 2 str
81 """A bytes which mostly acts as a Python 2 str
82
82
83 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
83 >>> bytestr(), bytestr(bytearray(b'foo')), bytestr(u'ascii'), bytestr(1)
84 (b'', b'foo', b'ascii', b'1')
84 (b'', b'foo', b'ascii', b'1')
85 >>> s = bytestr(b'foo')
85 >>> s = bytestr(b'foo')
86 >>> assert s is bytestr(s)
86 >>> assert s is bytestr(s)
87
87
88 There's no implicit conversion from non-ascii str as its encoding is
88 There's no implicit conversion from non-ascii str as its encoding is
89 unknown:
89 unknown:
90
90
91 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
91 >>> bytestr(chr(0x80)) # doctest: +ELLIPSIS
92 Traceback (most recent call last):
92 Traceback (most recent call last):
93 ...
93 ...
94 UnicodeEncodeError: ...
94 UnicodeEncodeError: ...
95
95
96 Comparison between bytestr and bytes should work:
96 Comparison between bytestr and bytes should work:
97
97
98 >>> assert bytestr(b'foo') == b'foo'
98 >>> assert bytestr(b'foo') == b'foo'
99 >>> assert b'foo' == bytestr(b'foo')
99 >>> assert b'foo' == bytestr(b'foo')
100 >>> assert b'f' in bytestr(b'foo')
100 >>> assert b'f' in bytestr(b'foo')
101 >>> assert bytestr(b'f') in b'foo'
101 >>> assert bytestr(b'f') in b'foo'
102
102
103 Sliced elements should be bytes, not integer:
103 Sliced elements should be bytes, not integer:
104
104
105 >>> s[1], s[:2]
105 >>> s[1], s[:2]
106 (b'o', b'fo')
106 (b'o', b'fo')
107 >>> list(s), list(reversed(s))
107 >>> list(s), list(reversed(s))
108 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
108 ([b'f', b'o', b'o'], [b'o', b'o', b'f'])
109
109
110 As bytestr type isn't propagated across operations, you need to cast
110 As bytestr type isn't propagated across operations, you need to cast
111 bytes to bytestr explicitly:
111 bytes to bytestr explicitly:
112
112
113 >>> s = bytestr(b'foo').upper()
113 >>> s = bytestr(b'foo').upper()
114 >>> t = bytestr(s)
114 >>> t = bytestr(s)
115 >>> s[0], t[0]
115 >>> s[0], t[0]
116 (70, b'F')
116 (70, b'F')
117
117
118 Be careful to not pass a bytestr object to a function which expects
118 Be careful to not pass a bytestr object to a function which expects
119 bytearray-like behavior.
119 bytearray-like behavior.
120
120
121 >>> t = bytes(t) # cast to bytes
121 >>> t = bytes(t) # cast to bytes
122 >>> assert type(t) is bytes
122 >>> assert type(t) is bytes
123 """
123 """
124
124
125 def __new__(cls, s=b''):
125 def __new__(cls, s=b''):
126 if isinstance(s, bytestr):
126 if isinstance(s, bytestr):
127 return s
127 return s
128 if not isinstance(s, (bytes, bytearray)):
128 if not isinstance(s, (bytes, bytearray)):
129 s = str(s).encode(u'ascii')
129 s = str(s).encode(u'ascii')
130 return bytes.__new__(cls, s)
130 return bytes.__new__(cls, s)
131
131
132 def __getitem__(self, key):
132 def __getitem__(self, key):
133 s = bytes.__getitem__(self, key)
133 s = bytes.__getitem__(self, key)
134 if not isinstance(s, bytes):
134 if not isinstance(s, bytes):
135 s = bytechr(s)
135 s = bytechr(s)
136 return s
136 return s
137
137
138 def __iter__(self):
138 def __iter__(self):
139 return iterbytestr(bytes.__iter__(self))
139 return iterbytestr(bytes.__iter__(self))
140
140
141 def iterbytestr(s):
141 def iterbytestr(s):
142 """Iterate bytes as if it were a str object of Python 2"""
142 """Iterate bytes as if it were a str object of Python 2"""
143 return map(bytechr, s)
143 return map(bytechr, s)
144
144
145 def sysbytes(s):
145 def sysbytes(s):
146 """Convert an internal str (e.g. keyword, __doc__) back to bytes
146 """Convert an internal str (e.g. keyword, __doc__) back to bytes
147
147
148 This never raises UnicodeEncodeError, but only ASCII characters
148 This never raises UnicodeEncodeError, but only ASCII characters
149 can be round-trip by sysstr(sysbytes(s)).
149 can be round-trip by sysstr(sysbytes(s)).
150 """
150 """
151 return s.encode(u'utf-8')
151 return s.encode(u'utf-8')
152
152
153 def sysstr(s):
153 def sysstr(s):
154 """Return a keyword str to be passed to Python functions such as
154 """Return a keyword str to be passed to Python functions such as
155 getattr() and str.encode()
155 getattr() and str.encode()
156
156
157 This never raises UnicodeDecodeError. Non-ascii characters are
157 This never raises UnicodeDecodeError. Non-ascii characters are
158 considered invalid and mapped to arbitrary but unique code points
158 considered invalid and mapped to arbitrary but unique code points
159 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
159 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
160 """
160 """
161 if isinstance(s, builtins.str):
161 if isinstance(s, builtins.str):
162 return s
162 return s
163 return s.decode(u'latin-1')
163 return s.decode(u'latin-1')
164
164
165 def _wrapattrfunc(f):
165 def _wrapattrfunc(f):
166 @functools.wraps(f)
166 @functools.wraps(f)
167 def w(object, name, *args):
167 def w(object, name, *args):
168 return f(object, sysstr(name), *args)
168 return f(object, sysstr(name), *args)
169 return w
169 return w
170
170
171 # these wrappers are automagically imported by hgloader
171 # these wrappers are automagically imported by hgloader
172 delattr = _wrapattrfunc(builtins.delattr)
172 delattr = _wrapattrfunc(builtins.delattr)
173 getattr = _wrapattrfunc(builtins.getattr)
173 getattr = _wrapattrfunc(builtins.getattr)
174 hasattr = _wrapattrfunc(builtins.hasattr)
174 hasattr = _wrapattrfunc(builtins.hasattr)
175 setattr = _wrapattrfunc(builtins.setattr)
175 setattr = _wrapattrfunc(builtins.setattr)
176 xrange = builtins.range
176 xrange = builtins.range
177 unicode = str
177
178
178 def open(name, mode='r', buffering=-1):
179 def open(name, mode='r', buffering=-1):
179 return builtins.open(name, sysstr(mode), buffering)
180 return builtins.open(name, sysstr(mode), buffering)
180
181
181 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
182 # getopt.getopt() on Python 3 deals with unicodes internally so we cannot
182 # pass bytes there. Passing unicodes will result in unicodes as return
183 # pass bytes there. Passing unicodes will result in unicodes as return
183 # values which we need to convert again to bytes.
184 # values which we need to convert again to bytes.
184 def getoptb(args, shortlist, namelist):
185 def getoptb(args, shortlist, namelist):
185 args = [a.decode('latin-1') for a in args]
186 args = [a.decode('latin-1') for a in args]
186 shortlist = shortlist.decode('latin-1')
187 shortlist = shortlist.decode('latin-1')
187 namelist = [a.decode('latin-1') for a in namelist]
188 namelist = [a.decode('latin-1') for a in namelist]
188 opts, args = getopt.getopt(args, shortlist, namelist)
189 opts, args = getopt.getopt(args, shortlist, namelist)
189 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
190 opts = [(a[0].encode('latin-1'), a[1].encode('latin-1'))
190 for a in opts]
191 for a in opts]
191 args = [a.encode('latin-1') for a in args]
192 args = [a.encode('latin-1') for a in args]
192 return opts, args
193 return opts, args
193
194
194 # keys of keyword arguments in Python need to be strings which are unicodes
195 # keys of keyword arguments in Python need to be strings which are unicodes
195 # Python 3. This function takes keyword arguments, convert the keys to str.
196 # Python 3. This function takes keyword arguments, convert the keys to str.
196 def strkwargs(dic):
197 def strkwargs(dic):
197 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
198 dic = dict((k.decode('latin-1'), v) for k, v in dic.iteritems())
198 return dic
199 return dic
199
200
200 # keys of keyword arguments need to be unicode while passing into
201 # keys of keyword arguments need to be unicode while passing into
201 # a function. This function helps us to convert those keys back to bytes
202 # a function. This function helps us to convert those keys back to bytes
202 # again as we need to deal with bytes.
203 # again as we need to deal with bytes.
203 def byteskwargs(dic):
204 def byteskwargs(dic):
204 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
205 dic = dict((k.encode('latin-1'), v) for k, v in dic.iteritems())
205 return dic
206 return dic
206
207
207 # shlex.split() accepts unicodes on Python 3. This function takes bytes
208 # shlex.split() accepts unicodes on Python 3. This function takes bytes
208 # argument, convert it into unicodes, pass into shlex.split(), convert the
209 # argument, convert it into unicodes, pass into shlex.split(), convert the
209 # returned value to bytes and return that.
210 # returned value to bytes and return that.
210 # TODO: handle shlex.shlex().
211 # TODO: handle shlex.shlex().
211 def shlexsplit(s):
212 def shlexsplit(s):
212 ret = shlex.split(s.decode('latin-1'))
213 ret = shlex.split(s.decode('latin-1'))
213 return [a.encode('latin-1') for a in ret]
214 return [a.encode('latin-1') for a in ret]
214
215
215 else:
216 else:
216 import cStringIO
217 import cStringIO
217
218
218 bytechr = chr
219 bytechr = chr
219 bytestr = str
220 bytestr = str
220 iterbytestr = iter
221 iterbytestr = iter
221 sysbytes = identity
222 sysbytes = identity
222 sysstr = identity
223 sysstr = identity
223
224
224 # Partial backport from os.py in Python 3, which only accepts bytes.
225 # Partial backport from os.py in Python 3, which only accepts bytes.
225 # In Python 2, our paths should only ever be bytes, a unicode path
226 # In Python 2, our paths should only ever be bytes, a unicode path
226 # indicates a bug.
227 # indicates a bug.
227 def fsencode(filename):
228 def fsencode(filename):
228 if isinstance(filename, str):
229 if isinstance(filename, str):
229 return filename
230 return filename
230 else:
231 else:
231 raise TypeError(
232 raise TypeError(
232 "expect str, not %s" % type(filename).__name__)
233 "expect str, not %s" % type(filename).__name__)
233
234
234 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
235 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
235 # better not to touch Python 2 part as it's already working fine.
236 # better not to touch Python 2 part as it's already working fine.
236 fsdecode = identity
237 fsdecode = identity
237
238
238 def getoptb(args, shortlist, namelist):
239 def getoptb(args, shortlist, namelist):
239 return getopt.getopt(args, shortlist, namelist)
240 return getopt.getopt(args, shortlist, namelist)
240
241
241 strkwargs = identity
242 strkwargs = identity
242 byteskwargs = identity
243 byteskwargs = identity
243
244
244 oslinesep = os.linesep
245 oslinesep = os.linesep
245 osname = os.name
246 osname = os.name
246 ospathsep = os.pathsep
247 ospathsep = os.pathsep
247 ossep = os.sep
248 ossep = os.sep
248 osaltsep = os.altsep
249 osaltsep = os.altsep
249 stdin = sys.stdin
250 stdin = sys.stdin
250 stdout = sys.stdout
251 stdout = sys.stdout
251 stderr = sys.stderr
252 stderr = sys.stderr
252 if getattr(sys, 'argv', None) is not None:
253 if getattr(sys, 'argv', None) is not None:
253 sysargv = sys.argv
254 sysargv = sys.argv
254 sysplatform = sys.platform
255 sysplatform = sys.platform
255 getcwd = os.getcwd
256 getcwd = os.getcwd
256 sysexecutable = sys.executable
257 sysexecutable = sys.executable
257 shlexsplit = shlex.split
258 shlexsplit = shlex.split
258 stringio = cStringIO.StringIO
259 stringio = cStringIO.StringIO
259 maplist = map
260 maplist = map
260
261
261 empty = _queue.Empty
262 empty = _queue.Empty
262 queue = _queue.Queue
263 queue = _queue.Queue
263
264
264 class _pycompatstub(object):
265 class _pycompatstub(object):
265 def __init__(self):
266 def __init__(self):
266 self._aliases = {}
267 self._aliases = {}
267
268
268 def _registeraliases(self, origin, items):
269 def _registeraliases(self, origin, items):
269 """Add items that will be populated at the first access"""
270 """Add items that will be populated at the first access"""
270 items = map(sysstr, items)
271 items = map(sysstr, items)
271 self._aliases.update(
272 self._aliases.update(
272 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
273 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
273 for item in items)
274 for item in items)
274
275
275 def _registeralias(self, origin, attr, name):
276 def _registeralias(self, origin, attr, name):
276 """Alias ``origin``.``attr`` as ``name``"""
277 """Alias ``origin``.``attr`` as ``name``"""
277 self._aliases[sysstr(name)] = (origin, sysstr(attr))
278 self._aliases[sysstr(name)] = (origin, sysstr(attr))
278
279
279 def __getattr__(self, name):
280 def __getattr__(self, name):
280 try:
281 try:
281 origin, item = self._aliases[name]
282 origin, item = self._aliases[name]
282 except KeyError:
283 except KeyError:
283 raise AttributeError(name)
284 raise AttributeError(name)
284 self.__dict__[name] = obj = getattr(origin, item)
285 self.__dict__[name] = obj = getattr(origin, item)
285 return obj
286 return obj
286
287
287 httpserver = _pycompatstub()
288 httpserver = _pycompatstub()
288 urlreq = _pycompatstub()
289 urlreq = _pycompatstub()
289 urlerr = _pycompatstub()
290 urlerr = _pycompatstub()
290 if not ispy3:
291 if not ispy3:
291 import BaseHTTPServer
292 import BaseHTTPServer
292 import CGIHTTPServer
293 import CGIHTTPServer
293 import SimpleHTTPServer
294 import SimpleHTTPServer
294 import urllib2
295 import urllib2
295 import urllib
296 import urllib
296 import urlparse
297 import urlparse
297 urlreq._registeraliases(urllib, (
298 urlreq._registeraliases(urllib, (
298 "addclosehook",
299 "addclosehook",
299 "addinfourl",
300 "addinfourl",
300 "ftpwrapper",
301 "ftpwrapper",
301 "pathname2url",
302 "pathname2url",
302 "quote",
303 "quote",
303 "splitattr",
304 "splitattr",
304 "splitpasswd",
305 "splitpasswd",
305 "splitport",
306 "splitport",
306 "splituser",
307 "splituser",
307 "unquote",
308 "unquote",
308 "url2pathname",
309 "url2pathname",
309 "urlencode",
310 "urlencode",
310 ))
311 ))
311 urlreq._registeraliases(urllib2, (
312 urlreq._registeraliases(urllib2, (
312 "AbstractHTTPHandler",
313 "AbstractHTTPHandler",
313 "BaseHandler",
314 "BaseHandler",
314 "build_opener",
315 "build_opener",
315 "FileHandler",
316 "FileHandler",
316 "FTPHandler",
317 "FTPHandler",
317 "HTTPBasicAuthHandler",
318 "HTTPBasicAuthHandler",
318 "HTTPDigestAuthHandler",
319 "HTTPDigestAuthHandler",
319 "HTTPHandler",
320 "HTTPHandler",
320 "HTTPPasswordMgrWithDefaultRealm",
321 "HTTPPasswordMgrWithDefaultRealm",
321 "HTTPSHandler",
322 "HTTPSHandler",
322 "install_opener",
323 "install_opener",
323 "ProxyHandler",
324 "ProxyHandler",
324 "Request",
325 "Request",
325 "urlopen",
326 "urlopen",
326 ))
327 ))
327 urlreq._registeraliases(urlparse, (
328 urlreq._registeraliases(urlparse, (
328 "urlparse",
329 "urlparse",
329 "urlunparse",
330 "urlunparse",
330 ))
331 ))
331 urlerr._registeraliases(urllib2, (
332 urlerr._registeraliases(urllib2, (
332 "HTTPError",
333 "HTTPError",
333 "URLError",
334 "URLError",
334 ))
335 ))
335 httpserver._registeraliases(BaseHTTPServer, (
336 httpserver._registeraliases(BaseHTTPServer, (
336 "HTTPServer",
337 "HTTPServer",
337 "BaseHTTPRequestHandler",
338 "BaseHTTPRequestHandler",
338 ))
339 ))
339 httpserver._registeraliases(SimpleHTTPServer, (
340 httpserver._registeraliases(SimpleHTTPServer, (
340 "SimpleHTTPRequestHandler",
341 "SimpleHTTPRequestHandler",
341 ))
342 ))
342 httpserver._registeraliases(CGIHTTPServer, (
343 httpserver._registeraliases(CGIHTTPServer, (
343 "CGIHTTPRequestHandler",
344 "CGIHTTPRequestHandler",
344 ))
345 ))
345
346
346 else:
347 else:
347 import urllib.parse
348 import urllib.parse
348 urlreq._registeraliases(urllib.parse, (
349 urlreq._registeraliases(urllib.parse, (
349 "splitattr",
350 "splitattr",
350 "splitpasswd",
351 "splitpasswd",
351 "splitport",
352 "splitport",
352 "splituser",
353 "splituser",
353 "urlparse",
354 "urlparse",
354 "urlunparse",
355 "urlunparse",
355 ))
356 ))
356 urlreq._registeralias(urllib.parse, "unquote_to_bytes", "unquote")
357 urlreq._registeralias(urllib.parse, "unquote_to_bytes", "unquote")
357 import urllib.request
358 import urllib.request
358 urlreq._registeraliases(urllib.request, (
359 urlreq._registeraliases(urllib.request, (
359 "AbstractHTTPHandler",
360 "AbstractHTTPHandler",
360 "BaseHandler",
361 "BaseHandler",
361 "build_opener",
362 "build_opener",
362 "FileHandler",
363 "FileHandler",
363 "FTPHandler",
364 "FTPHandler",
364 "ftpwrapper",
365 "ftpwrapper",
365 "HTTPHandler",
366 "HTTPHandler",
366 "HTTPSHandler",
367 "HTTPSHandler",
367 "install_opener",
368 "install_opener",
368 "pathname2url",
369 "pathname2url",
369 "HTTPBasicAuthHandler",
370 "HTTPBasicAuthHandler",
370 "HTTPDigestAuthHandler",
371 "HTTPDigestAuthHandler",
371 "HTTPPasswordMgrWithDefaultRealm",
372 "HTTPPasswordMgrWithDefaultRealm",
372 "ProxyHandler",
373 "ProxyHandler",
373 "Request",
374 "Request",
374 "url2pathname",
375 "url2pathname",
375 "urlopen",
376 "urlopen",
376 ))
377 ))
377 import urllib.response
378 import urllib.response
378 urlreq._registeraliases(urllib.response, (
379 urlreq._registeraliases(urllib.response, (
379 "addclosehook",
380 "addclosehook",
380 "addinfourl",
381 "addinfourl",
381 ))
382 ))
382 import urllib.error
383 import urllib.error
383 urlerr._registeraliases(urllib.error, (
384 urlerr._registeraliases(urllib.error, (
384 "HTTPError",
385 "HTTPError",
385 "URLError",
386 "URLError",
386 ))
387 ))
387 import http.server
388 import http.server
388 httpserver._registeraliases(http.server, (
389 httpserver._registeraliases(http.server, (
389 "HTTPServer",
390 "HTTPServer",
390 "BaseHTTPRequestHandler",
391 "BaseHTTPRequestHandler",
391 "SimpleHTTPRequestHandler",
392 "SimpleHTTPRequestHandler",
392 "CGIHTTPRequestHandler",
393 "CGIHTTPRequestHandler",
393 ))
394 ))
394
395
395 # urllib.parse.quote() accepts both str and bytes, decodes bytes
396 # urllib.parse.quote() accepts both str and bytes, decodes bytes
396 # (if necessary), and returns str. This is wonky. We provide a custom
397 # (if necessary), and returns str. This is wonky. We provide a custom
397 # implementation that only accepts bytes and emits bytes.
398 # implementation that only accepts bytes and emits bytes.
398 def quote(s, safe=r'/'):
399 def quote(s, safe=r'/'):
399 s = urllib.parse.quote_from_bytes(s, safe=safe)
400 s = urllib.parse.quote_from_bytes(s, safe=safe)
400 return s.encode('ascii', 'strict')
401 return s.encode('ascii', 'strict')
401
402
402 # urllib.parse.urlencode() returns str. We use this function to make
403 # urllib.parse.urlencode() returns str. We use this function to make
403 # sure we return bytes.
404 # sure we return bytes.
404 def urlencode(query, doseq=False):
405 def urlencode(query, doseq=False):
405 s = urllib.parse.urlencode(query, doseq=doseq)
406 s = urllib.parse.urlencode(query, doseq=doseq)
406 return s.encode('ascii')
407 return s.encode('ascii')
407
408
408 urlreq.quote = quote
409 urlreq.quote = quote
409 urlreq.urlencode = urlencode
410 urlreq.urlencode = urlencode
General Comments 0
You need to be logged in to leave comments. Login now