##// END OF EJS Templates
py3: add os.getcwdb() to have bytes path...
Pulkit Goyal -
r30500:fc0cfe6c default
parent child Browse files
Show More
@@ -1,975 +1,975 b''
1 # dispatch.py - command dispatching for mercurial
1 # dispatch.py - command dispatching for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.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, print_function
8 from __future__ import absolute_import, print_function
9
9
10 import atexit
10 import atexit
11 import difflib
11 import difflib
12 import errno
12 import errno
13 import os
13 import os
14 import pdb
14 import pdb
15 import re
15 import re
16 import shlex
16 import shlex
17 import signal
17 import signal
18 import socket
18 import socket
19 import sys
19 import sys
20 import time
20 import time
21 import traceback
21 import traceback
22
22
23
23
24 from .i18n import _
24 from .i18n import _
25
25
26 from . import (
26 from . import (
27 cmdutil,
27 cmdutil,
28 commands,
28 commands,
29 debugcommands,
29 debugcommands,
30 demandimport,
30 demandimport,
31 encoding,
31 encoding,
32 error,
32 error,
33 extensions,
33 extensions,
34 fancyopts,
34 fancyopts,
35 fileset,
35 fileset,
36 hg,
36 hg,
37 hook,
37 hook,
38 profiling,
38 profiling,
39 pycompat,
39 pycompat,
40 revset,
40 revset,
41 templatefilters,
41 templatefilters,
42 templatekw,
42 templatekw,
43 templater,
43 templater,
44 ui as uimod,
44 ui as uimod,
45 util,
45 util,
46 )
46 )
47
47
48 class request(object):
48 class request(object):
49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
49 def __init__(self, args, ui=None, repo=None, fin=None, fout=None,
50 ferr=None):
50 ferr=None):
51 self.args = args
51 self.args = args
52 self.ui = ui
52 self.ui = ui
53 self.repo = repo
53 self.repo = repo
54
54
55 # input/output/error streams
55 # input/output/error streams
56 self.fin = fin
56 self.fin = fin
57 self.fout = fout
57 self.fout = fout
58 self.ferr = ferr
58 self.ferr = ferr
59
59
60 def run():
60 def run():
61 "run the command in sys.argv"
61 "run the command in sys.argv"
62 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
62 sys.exit((dispatch(request(pycompat.sysargv[1:])) or 0) & 255)
63
63
64 def _getsimilar(symbols, value):
64 def _getsimilar(symbols, value):
65 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
65 sim = lambda x: difflib.SequenceMatcher(None, value, x).ratio()
66 # The cutoff for similarity here is pretty arbitrary. It should
66 # The cutoff for similarity here is pretty arbitrary. It should
67 # probably be investigated and tweaked.
67 # probably be investigated and tweaked.
68 return [s for s in symbols if sim(s) > 0.6]
68 return [s for s in symbols if sim(s) > 0.6]
69
69
70 def _reportsimilar(write, similar):
70 def _reportsimilar(write, similar):
71 if len(similar) == 1:
71 if len(similar) == 1:
72 write(_("(did you mean %s?)\n") % similar[0])
72 write(_("(did you mean %s?)\n") % similar[0])
73 elif similar:
73 elif similar:
74 ss = ", ".join(sorted(similar))
74 ss = ", ".join(sorted(similar))
75 write(_("(did you mean one of %s?)\n") % ss)
75 write(_("(did you mean one of %s?)\n") % ss)
76
76
77 def _formatparse(write, inst):
77 def _formatparse(write, inst):
78 similar = []
78 similar = []
79 if isinstance(inst, error.UnknownIdentifier):
79 if isinstance(inst, error.UnknownIdentifier):
80 # make sure to check fileset first, as revset can invoke fileset
80 # make sure to check fileset first, as revset can invoke fileset
81 similar = _getsimilar(inst.symbols, inst.function)
81 similar = _getsimilar(inst.symbols, inst.function)
82 if len(inst.args) > 1:
82 if len(inst.args) > 1:
83 write(_("hg: parse error at %s: %s\n") %
83 write(_("hg: parse error at %s: %s\n") %
84 (inst.args[1], inst.args[0]))
84 (inst.args[1], inst.args[0]))
85 if (inst.args[0][0] == ' '):
85 if (inst.args[0][0] == ' '):
86 write(_("unexpected leading whitespace\n"))
86 write(_("unexpected leading whitespace\n"))
87 else:
87 else:
88 write(_("hg: parse error: %s\n") % inst.args[0])
88 write(_("hg: parse error: %s\n") % inst.args[0])
89 _reportsimilar(write, similar)
89 _reportsimilar(write, similar)
90 if inst.hint:
90 if inst.hint:
91 write(_("(%s)\n") % inst.hint)
91 write(_("(%s)\n") % inst.hint)
92
92
93 def dispatch(req):
93 def dispatch(req):
94 "run the command specified in req.args"
94 "run the command specified in req.args"
95 if req.ferr:
95 if req.ferr:
96 ferr = req.ferr
96 ferr = req.ferr
97 elif req.ui:
97 elif req.ui:
98 ferr = req.ui.ferr
98 ferr = req.ui.ferr
99 else:
99 else:
100 ferr = util.stderr
100 ferr = util.stderr
101
101
102 try:
102 try:
103 if not req.ui:
103 if not req.ui:
104 req.ui = uimod.ui()
104 req.ui = uimod.ui()
105 if '--traceback' in req.args:
105 if '--traceback' in req.args:
106 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
106 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
107
107
108 # set ui streams from the request
108 # set ui streams from the request
109 if req.fin:
109 if req.fin:
110 req.ui.fin = req.fin
110 req.ui.fin = req.fin
111 if req.fout:
111 if req.fout:
112 req.ui.fout = req.fout
112 req.ui.fout = req.fout
113 if req.ferr:
113 if req.ferr:
114 req.ui.ferr = req.ferr
114 req.ui.ferr = req.ferr
115 except error.Abort as inst:
115 except error.Abort as inst:
116 ferr.write(_("abort: %s\n") % inst)
116 ferr.write(_("abort: %s\n") % inst)
117 if inst.hint:
117 if inst.hint:
118 ferr.write(_("(%s)\n") % inst.hint)
118 ferr.write(_("(%s)\n") % inst.hint)
119 return -1
119 return -1
120 except error.ParseError as inst:
120 except error.ParseError as inst:
121 _formatparse(ferr.write, inst)
121 _formatparse(ferr.write, inst)
122 return -1
122 return -1
123
123
124 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
124 msg = ' '.join(' ' in a and repr(a) or a for a in req.args)
125 starttime = time.time()
125 starttime = time.time()
126 ret = None
126 ret = None
127 try:
127 try:
128 ret = _runcatch(req)
128 ret = _runcatch(req)
129 except KeyboardInterrupt:
129 except KeyboardInterrupt:
130 try:
130 try:
131 req.ui.warn(_("interrupted!\n"))
131 req.ui.warn(_("interrupted!\n"))
132 except IOError as inst:
132 except IOError as inst:
133 if inst.errno != errno.EPIPE:
133 if inst.errno != errno.EPIPE:
134 raise
134 raise
135 ret = -1
135 ret = -1
136 finally:
136 finally:
137 duration = time.time() - starttime
137 duration = time.time() - starttime
138 req.ui.flush()
138 req.ui.flush()
139 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
139 req.ui.log("commandfinish", "%s exited %s after %0.2f seconds\n",
140 msg, ret or 0, duration)
140 msg, ret or 0, duration)
141 return ret
141 return ret
142
142
143 def _runcatch(req):
143 def _runcatch(req):
144 def catchterm(*args):
144 def catchterm(*args):
145 raise error.SignalInterrupt
145 raise error.SignalInterrupt
146
146
147 ui = req.ui
147 ui = req.ui
148 try:
148 try:
149 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
149 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
150 num = getattr(signal, name, None)
150 num = getattr(signal, name, None)
151 if num:
151 if num:
152 signal.signal(num, catchterm)
152 signal.signal(num, catchterm)
153 except ValueError:
153 except ValueError:
154 pass # happens if called in a thread
154 pass # happens if called in a thread
155
155
156 def _runcatchfunc():
156 def _runcatchfunc():
157 try:
157 try:
158 debugger = 'pdb'
158 debugger = 'pdb'
159 debugtrace = {
159 debugtrace = {
160 'pdb' : pdb.set_trace
160 'pdb' : pdb.set_trace
161 }
161 }
162 debugmortem = {
162 debugmortem = {
163 'pdb' : pdb.post_mortem
163 'pdb' : pdb.post_mortem
164 }
164 }
165
165
166 # read --config before doing anything else
166 # read --config before doing anything else
167 # (e.g. to change trust settings for reading .hg/hgrc)
167 # (e.g. to change trust settings for reading .hg/hgrc)
168 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
168 cfgs = _parseconfig(req.ui, _earlygetopt(['--config'], req.args))
169
169
170 if req.repo:
170 if req.repo:
171 # copy configs that were passed on the cmdline (--config) to
171 # copy configs that were passed on the cmdline (--config) to
172 # the repo ui
172 # the repo ui
173 for sec, name, val in cfgs:
173 for sec, name, val in cfgs:
174 req.repo.ui.setconfig(sec, name, val, source='--config')
174 req.repo.ui.setconfig(sec, name, val, source='--config')
175
175
176 # developer config: ui.debugger
176 # developer config: ui.debugger
177 debugger = ui.config("ui", "debugger")
177 debugger = ui.config("ui", "debugger")
178 debugmod = pdb
178 debugmod = pdb
179 if not debugger or ui.plain():
179 if not debugger or ui.plain():
180 # if we are in HGPLAIN mode, then disable custom debugging
180 # if we are in HGPLAIN mode, then disable custom debugging
181 debugger = 'pdb'
181 debugger = 'pdb'
182 elif '--debugger' in req.args:
182 elif '--debugger' in req.args:
183 # This import can be slow for fancy debuggers, so only
183 # This import can be slow for fancy debuggers, so only
184 # do it when absolutely necessary, i.e. when actual
184 # do it when absolutely necessary, i.e. when actual
185 # debugging has been requested
185 # debugging has been requested
186 with demandimport.deactivated():
186 with demandimport.deactivated():
187 try:
187 try:
188 debugmod = __import__(debugger)
188 debugmod = __import__(debugger)
189 except ImportError:
189 except ImportError:
190 pass # Leave debugmod = pdb
190 pass # Leave debugmod = pdb
191
191
192 debugtrace[debugger] = debugmod.set_trace
192 debugtrace[debugger] = debugmod.set_trace
193 debugmortem[debugger] = debugmod.post_mortem
193 debugmortem[debugger] = debugmod.post_mortem
194
194
195 # enter the debugger before command execution
195 # enter the debugger before command execution
196 if '--debugger' in req.args:
196 if '--debugger' in req.args:
197 ui.warn(_("entering debugger - "
197 ui.warn(_("entering debugger - "
198 "type c to continue starting hg or h for help\n"))
198 "type c to continue starting hg or h for help\n"))
199
199
200 if (debugger != 'pdb' and
200 if (debugger != 'pdb' and
201 debugtrace[debugger] == debugtrace['pdb']):
201 debugtrace[debugger] == debugtrace['pdb']):
202 ui.warn(_("%s debugger specified "
202 ui.warn(_("%s debugger specified "
203 "but its module was not found\n") % debugger)
203 "but its module was not found\n") % debugger)
204 with demandimport.deactivated():
204 with demandimport.deactivated():
205 debugtrace[debugger]()
205 debugtrace[debugger]()
206 try:
206 try:
207 return _dispatch(req)
207 return _dispatch(req)
208 finally:
208 finally:
209 ui.flush()
209 ui.flush()
210 except: # re-raises
210 except: # re-raises
211 # enter the debugger when we hit an exception
211 # enter the debugger when we hit an exception
212 if '--debugger' in req.args:
212 if '--debugger' in req.args:
213 traceback.print_exc()
213 traceback.print_exc()
214 debugmortem[debugger](sys.exc_info()[2])
214 debugmortem[debugger](sys.exc_info()[2])
215 ui.traceback()
215 ui.traceback()
216 raise
216 raise
217
217
218 return callcatch(ui, _runcatchfunc)
218 return callcatch(ui, _runcatchfunc)
219
219
220 def callcatch(ui, func):
220 def callcatch(ui, func):
221 """call func() with global exception handling
221 """call func() with global exception handling
222
222
223 return func() if no exception happens. otherwise do some error handling
223 return func() if no exception happens. otherwise do some error handling
224 and return an exit code accordingly.
224 and return an exit code accordingly.
225 """
225 """
226 try:
226 try:
227 return func()
227 return func()
228 # Global exception handling, alphabetically
228 # Global exception handling, alphabetically
229 # Mercurial-specific first, followed by built-in and library exceptions
229 # Mercurial-specific first, followed by built-in and library exceptions
230 except error.AmbiguousCommand as inst:
230 except error.AmbiguousCommand as inst:
231 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
231 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
232 (inst.args[0], " ".join(inst.args[1])))
232 (inst.args[0], " ".join(inst.args[1])))
233 except error.ParseError as inst:
233 except error.ParseError as inst:
234 _formatparse(ui.warn, inst)
234 _formatparse(ui.warn, inst)
235 return -1
235 return -1
236 except error.LockHeld as inst:
236 except error.LockHeld as inst:
237 if inst.errno == errno.ETIMEDOUT:
237 if inst.errno == errno.ETIMEDOUT:
238 reason = _('timed out waiting for lock held by %s') % inst.locker
238 reason = _('timed out waiting for lock held by %s') % inst.locker
239 else:
239 else:
240 reason = _('lock held by %s') % inst.locker
240 reason = _('lock held by %s') % inst.locker
241 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
241 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
242 except error.LockUnavailable as inst:
242 except error.LockUnavailable as inst:
243 ui.warn(_("abort: could not lock %s: %s\n") %
243 ui.warn(_("abort: could not lock %s: %s\n") %
244 (inst.desc or inst.filename, inst.strerror))
244 (inst.desc or inst.filename, inst.strerror))
245 except error.CommandError as inst:
245 except error.CommandError as inst:
246 if inst.args[0]:
246 if inst.args[0]:
247 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
247 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
248 commands.help_(ui, inst.args[0], full=False, command=True)
248 commands.help_(ui, inst.args[0], full=False, command=True)
249 else:
249 else:
250 ui.warn(_("hg: %s\n") % inst.args[1])
250 ui.warn(_("hg: %s\n") % inst.args[1])
251 commands.help_(ui, 'shortlist')
251 commands.help_(ui, 'shortlist')
252 except error.OutOfBandError as inst:
252 except error.OutOfBandError as inst:
253 if inst.args:
253 if inst.args:
254 msg = _("abort: remote error:\n")
254 msg = _("abort: remote error:\n")
255 else:
255 else:
256 msg = _("abort: remote error\n")
256 msg = _("abort: remote error\n")
257 ui.warn(msg)
257 ui.warn(msg)
258 if inst.args:
258 if inst.args:
259 ui.warn(''.join(inst.args))
259 ui.warn(''.join(inst.args))
260 if inst.hint:
260 if inst.hint:
261 ui.warn('(%s)\n' % inst.hint)
261 ui.warn('(%s)\n' % inst.hint)
262 except error.RepoError as inst:
262 except error.RepoError as inst:
263 ui.warn(_("abort: %s!\n") % inst)
263 ui.warn(_("abort: %s!\n") % inst)
264 if inst.hint:
264 if inst.hint:
265 ui.warn(_("(%s)\n") % inst.hint)
265 ui.warn(_("(%s)\n") % inst.hint)
266 except error.ResponseError as inst:
266 except error.ResponseError as inst:
267 ui.warn(_("abort: %s") % inst.args[0])
267 ui.warn(_("abort: %s") % inst.args[0])
268 if not isinstance(inst.args[1], basestring):
268 if not isinstance(inst.args[1], basestring):
269 ui.warn(" %r\n" % (inst.args[1],))
269 ui.warn(" %r\n" % (inst.args[1],))
270 elif not inst.args[1]:
270 elif not inst.args[1]:
271 ui.warn(_(" empty string\n"))
271 ui.warn(_(" empty string\n"))
272 else:
272 else:
273 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
273 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
274 except error.CensoredNodeError as inst:
274 except error.CensoredNodeError as inst:
275 ui.warn(_("abort: file censored %s!\n") % inst)
275 ui.warn(_("abort: file censored %s!\n") % inst)
276 except error.RevlogError as inst:
276 except error.RevlogError as inst:
277 ui.warn(_("abort: %s!\n") % inst)
277 ui.warn(_("abort: %s!\n") % inst)
278 except error.SignalInterrupt:
278 except error.SignalInterrupt:
279 ui.warn(_("killed!\n"))
279 ui.warn(_("killed!\n"))
280 except error.UnknownCommand as inst:
280 except error.UnknownCommand as inst:
281 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
281 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
282 try:
282 try:
283 # check if the command is in a disabled extension
283 # check if the command is in a disabled extension
284 # (but don't check for extensions themselves)
284 # (but don't check for extensions themselves)
285 commands.help_(ui, inst.args[0], unknowncmd=True)
285 commands.help_(ui, inst.args[0], unknowncmd=True)
286 except (error.UnknownCommand, error.Abort):
286 except (error.UnknownCommand, error.Abort):
287 suggested = False
287 suggested = False
288 if len(inst.args) == 2:
288 if len(inst.args) == 2:
289 sim = _getsimilar(inst.args[1], inst.args[0])
289 sim = _getsimilar(inst.args[1], inst.args[0])
290 if sim:
290 if sim:
291 _reportsimilar(ui.warn, sim)
291 _reportsimilar(ui.warn, sim)
292 suggested = True
292 suggested = True
293 if not suggested:
293 if not suggested:
294 commands.help_(ui, 'shortlist')
294 commands.help_(ui, 'shortlist')
295 except error.InterventionRequired as inst:
295 except error.InterventionRequired as inst:
296 ui.warn("%s\n" % inst)
296 ui.warn("%s\n" % inst)
297 if inst.hint:
297 if inst.hint:
298 ui.warn(_("(%s)\n") % inst.hint)
298 ui.warn(_("(%s)\n") % inst.hint)
299 return 1
299 return 1
300 except error.Abort as inst:
300 except error.Abort as inst:
301 ui.warn(_("abort: %s\n") % inst)
301 ui.warn(_("abort: %s\n") % inst)
302 if inst.hint:
302 if inst.hint:
303 ui.warn(_("(%s)\n") % inst.hint)
303 ui.warn(_("(%s)\n") % inst.hint)
304 except ImportError as inst:
304 except ImportError as inst:
305 ui.warn(_("abort: %s!\n") % inst)
305 ui.warn(_("abort: %s!\n") % inst)
306 m = str(inst).split()[-1]
306 m = str(inst).split()[-1]
307 if m in "mpatch bdiff".split():
307 if m in "mpatch bdiff".split():
308 ui.warn(_("(did you forget to compile extensions?)\n"))
308 ui.warn(_("(did you forget to compile extensions?)\n"))
309 elif m in "zlib".split():
309 elif m in "zlib".split():
310 ui.warn(_("(is your Python install correct?)\n"))
310 ui.warn(_("(is your Python install correct?)\n"))
311 except IOError as inst:
311 except IOError as inst:
312 if util.safehasattr(inst, "code"):
312 if util.safehasattr(inst, "code"):
313 ui.warn(_("abort: %s\n") % inst)
313 ui.warn(_("abort: %s\n") % inst)
314 elif util.safehasattr(inst, "reason"):
314 elif util.safehasattr(inst, "reason"):
315 try: # usually it is in the form (errno, strerror)
315 try: # usually it is in the form (errno, strerror)
316 reason = inst.reason.args[1]
316 reason = inst.reason.args[1]
317 except (AttributeError, IndexError):
317 except (AttributeError, IndexError):
318 # it might be anything, for example a string
318 # it might be anything, for example a string
319 reason = inst.reason
319 reason = inst.reason
320 if isinstance(reason, unicode):
320 if isinstance(reason, unicode):
321 # SSLError of Python 2.7.9 contains a unicode
321 # SSLError of Python 2.7.9 contains a unicode
322 reason = reason.encode(encoding.encoding, 'replace')
322 reason = reason.encode(encoding.encoding, 'replace')
323 ui.warn(_("abort: error: %s\n") % reason)
323 ui.warn(_("abort: error: %s\n") % reason)
324 elif (util.safehasattr(inst, "args")
324 elif (util.safehasattr(inst, "args")
325 and inst.args and inst.args[0] == errno.EPIPE):
325 and inst.args and inst.args[0] == errno.EPIPE):
326 pass
326 pass
327 elif getattr(inst, "strerror", None):
327 elif getattr(inst, "strerror", None):
328 if getattr(inst, "filename", None):
328 if getattr(inst, "filename", None):
329 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
329 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
330 else:
330 else:
331 ui.warn(_("abort: %s\n") % inst.strerror)
331 ui.warn(_("abort: %s\n") % inst.strerror)
332 else:
332 else:
333 raise
333 raise
334 except OSError as inst:
334 except OSError as inst:
335 if getattr(inst, "filename", None) is not None:
335 if getattr(inst, "filename", None) is not None:
336 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
336 ui.warn(_("abort: %s: '%s'\n") % (inst.strerror, inst.filename))
337 else:
337 else:
338 ui.warn(_("abort: %s\n") % inst.strerror)
338 ui.warn(_("abort: %s\n") % inst.strerror)
339 except KeyboardInterrupt:
339 except KeyboardInterrupt:
340 raise
340 raise
341 except MemoryError:
341 except MemoryError:
342 ui.warn(_("abort: out of memory\n"))
342 ui.warn(_("abort: out of memory\n"))
343 except SystemExit as inst:
343 except SystemExit as inst:
344 # Commands shouldn't sys.exit directly, but give a return code.
344 # Commands shouldn't sys.exit directly, but give a return code.
345 # Just in case catch this and and pass exit code to caller.
345 # Just in case catch this and and pass exit code to caller.
346 return inst.code
346 return inst.code
347 except socket.error as inst:
347 except socket.error as inst:
348 ui.warn(_("abort: %s\n") % inst.args[-1])
348 ui.warn(_("abort: %s\n") % inst.args[-1])
349 except: # perhaps re-raises
349 except: # perhaps re-raises
350 if not handlecommandexception(ui):
350 if not handlecommandexception(ui):
351 raise
351 raise
352
352
353 return -1
353 return -1
354
354
355 def aliasargs(fn, givenargs):
355 def aliasargs(fn, givenargs):
356 args = getattr(fn, 'args', [])
356 args = getattr(fn, 'args', [])
357 if args:
357 if args:
358 cmd = ' '.join(map(util.shellquote, args))
358 cmd = ' '.join(map(util.shellquote, args))
359
359
360 nums = []
360 nums = []
361 def replacer(m):
361 def replacer(m):
362 num = int(m.group(1)) - 1
362 num = int(m.group(1)) - 1
363 nums.append(num)
363 nums.append(num)
364 if num < len(givenargs):
364 if num < len(givenargs):
365 return givenargs[num]
365 return givenargs[num]
366 raise error.Abort(_('too few arguments for command alias'))
366 raise error.Abort(_('too few arguments for command alias'))
367 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
367 cmd = re.sub(r'\$(\d+|\$)', replacer, cmd)
368 givenargs = [x for i, x in enumerate(givenargs)
368 givenargs = [x for i, x in enumerate(givenargs)
369 if i not in nums]
369 if i not in nums]
370 args = shlex.split(cmd)
370 args = shlex.split(cmd)
371 return args + givenargs
371 return args + givenargs
372
372
373 def aliasinterpolate(name, args, cmd):
373 def aliasinterpolate(name, args, cmd):
374 '''interpolate args into cmd for shell aliases
374 '''interpolate args into cmd for shell aliases
375
375
376 This also handles $0, $@ and "$@".
376 This also handles $0, $@ and "$@".
377 '''
377 '''
378 # util.interpolate can't deal with "$@" (with quotes) because it's only
378 # util.interpolate can't deal with "$@" (with quotes) because it's only
379 # built to match prefix + patterns.
379 # built to match prefix + patterns.
380 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
380 replacemap = dict(('$%d' % (i + 1), arg) for i, arg in enumerate(args))
381 replacemap['$0'] = name
381 replacemap['$0'] = name
382 replacemap['$$'] = '$'
382 replacemap['$$'] = '$'
383 replacemap['$@'] = ' '.join(args)
383 replacemap['$@'] = ' '.join(args)
384 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
384 # Typical Unix shells interpolate "$@" (with quotes) as all the positional
385 # parameters, separated out into words. Emulate the same behavior here by
385 # parameters, separated out into words. Emulate the same behavior here by
386 # quoting the arguments individually. POSIX shells will then typically
386 # quoting the arguments individually. POSIX shells will then typically
387 # tokenize each argument into exactly one word.
387 # tokenize each argument into exactly one word.
388 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
388 replacemap['"$@"'] = ' '.join(util.shellquote(arg) for arg in args)
389 # escape '\$' for regex
389 # escape '\$' for regex
390 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
390 regex = '|'.join(replacemap.keys()).replace('$', r'\$')
391 r = re.compile(regex)
391 r = re.compile(regex)
392 return r.sub(lambda x: replacemap[x.group()], cmd)
392 return r.sub(lambda x: replacemap[x.group()], cmd)
393
393
394 class cmdalias(object):
394 class cmdalias(object):
395 def __init__(self, name, definition, cmdtable, source):
395 def __init__(self, name, definition, cmdtable, source):
396 self.name = self.cmd = name
396 self.name = self.cmd = name
397 self.cmdname = ''
397 self.cmdname = ''
398 self.definition = definition
398 self.definition = definition
399 self.fn = None
399 self.fn = None
400 self.givenargs = []
400 self.givenargs = []
401 self.opts = []
401 self.opts = []
402 self.help = ''
402 self.help = ''
403 self.badalias = None
403 self.badalias = None
404 self.unknowncmd = False
404 self.unknowncmd = False
405 self.source = source
405 self.source = source
406
406
407 try:
407 try:
408 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
408 aliases, entry = cmdutil.findcmd(self.name, cmdtable)
409 for alias, e in cmdtable.iteritems():
409 for alias, e in cmdtable.iteritems():
410 if e is entry:
410 if e is entry:
411 self.cmd = alias
411 self.cmd = alias
412 break
412 break
413 self.shadows = True
413 self.shadows = True
414 except error.UnknownCommand:
414 except error.UnknownCommand:
415 self.shadows = False
415 self.shadows = False
416
416
417 if not self.definition:
417 if not self.definition:
418 self.badalias = _("no definition for alias '%s'") % self.name
418 self.badalias = _("no definition for alias '%s'") % self.name
419 return
419 return
420
420
421 if self.definition.startswith('!'):
421 if self.definition.startswith('!'):
422 self.shell = True
422 self.shell = True
423 def fn(ui, *args):
423 def fn(ui, *args):
424 env = {'HG_ARGS': ' '.join((self.name,) + args)}
424 env = {'HG_ARGS': ' '.join((self.name,) + args)}
425 def _checkvar(m):
425 def _checkvar(m):
426 if m.groups()[0] == '$':
426 if m.groups()[0] == '$':
427 return m.group()
427 return m.group()
428 elif int(m.groups()[0]) <= len(args):
428 elif int(m.groups()[0]) <= len(args):
429 return m.group()
429 return m.group()
430 else:
430 else:
431 ui.debug("No argument found for substitution "
431 ui.debug("No argument found for substitution "
432 "of %i variable in alias '%s' definition."
432 "of %i variable in alias '%s' definition."
433 % (int(m.groups()[0]), self.name))
433 % (int(m.groups()[0]), self.name))
434 return ''
434 return ''
435 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
435 cmd = re.sub(r'\$(\d+|\$)', _checkvar, self.definition[1:])
436 cmd = aliasinterpolate(self.name, args, cmd)
436 cmd = aliasinterpolate(self.name, args, cmd)
437 return ui.system(cmd, environ=env)
437 return ui.system(cmd, environ=env)
438 self.fn = fn
438 self.fn = fn
439 return
439 return
440
440
441 try:
441 try:
442 args = shlex.split(self.definition)
442 args = shlex.split(self.definition)
443 except ValueError as inst:
443 except ValueError as inst:
444 self.badalias = (_("error in definition for alias '%s': %s")
444 self.badalias = (_("error in definition for alias '%s': %s")
445 % (self.name, inst))
445 % (self.name, inst))
446 return
446 return
447 self.cmdname = cmd = args.pop(0)
447 self.cmdname = cmd = args.pop(0)
448 self.givenargs = args
448 self.givenargs = args
449
449
450 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
450 for invalidarg in ("--cwd", "-R", "--repository", "--repo", "--config"):
451 if _earlygetopt([invalidarg], args):
451 if _earlygetopt([invalidarg], args):
452 self.badalias = (_("error in definition for alias '%s': %s may "
452 self.badalias = (_("error in definition for alias '%s': %s may "
453 "only be given on the command line")
453 "only be given on the command line")
454 % (self.name, invalidarg))
454 % (self.name, invalidarg))
455 return
455 return
456
456
457 try:
457 try:
458 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
458 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
459 if len(tableentry) > 2:
459 if len(tableentry) > 2:
460 self.fn, self.opts, self.help = tableentry
460 self.fn, self.opts, self.help = tableentry
461 else:
461 else:
462 self.fn, self.opts = tableentry
462 self.fn, self.opts = tableentry
463
463
464 if self.help.startswith("hg " + cmd):
464 if self.help.startswith("hg " + cmd):
465 # drop prefix in old-style help lines so hg shows the alias
465 # drop prefix in old-style help lines so hg shows the alias
466 self.help = self.help[4 + len(cmd):]
466 self.help = self.help[4 + len(cmd):]
467 self.__doc__ = self.fn.__doc__
467 self.__doc__ = self.fn.__doc__
468
468
469 except error.UnknownCommand:
469 except error.UnknownCommand:
470 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
470 self.badalias = (_("alias '%s' resolves to unknown command '%s'")
471 % (self.name, cmd))
471 % (self.name, cmd))
472 self.unknowncmd = True
472 self.unknowncmd = True
473 except error.AmbiguousCommand:
473 except error.AmbiguousCommand:
474 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
474 self.badalias = (_("alias '%s' resolves to ambiguous command '%s'")
475 % (self.name, cmd))
475 % (self.name, cmd))
476
476
477 @property
477 @property
478 def args(self):
478 def args(self):
479 args = map(util.expandpath, self.givenargs)
479 args = map(util.expandpath, self.givenargs)
480 return aliasargs(self.fn, args)
480 return aliasargs(self.fn, args)
481
481
482 def __getattr__(self, name):
482 def __getattr__(self, name):
483 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
483 adefaults = {'norepo': True, 'optionalrepo': False, 'inferrepo': False}
484 if name not in adefaults:
484 if name not in adefaults:
485 raise AttributeError(name)
485 raise AttributeError(name)
486 if self.badalias or util.safehasattr(self, 'shell'):
486 if self.badalias or util.safehasattr(self, 'shell'):
487 return adefaults[name]
487 return adefaults[name]
488 return getattr(self.fn, name)
488 return getattr(self.fn, name)
489
489
490 def __call__(self, ui, *args, **opts):
490 def __call__(self, ui, *args, **opts):
491 if self.badalias:
491 if self.badalias:
492 hint = None
492 hint = None
493 if self.unknowncmd:
493 if self.unknowncmd:
494 try:
494 try:
495 # check if the command is in a disabled extension
495 # check if the command is in a disabled extension
496 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
496 cmd, ext = extensions.disabledcmd(ui, self.cmdname)[:2]
497 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
497 hint = _("'%s' is provided by '%s' extension") % (cmd, ext)
498 except error.UnknownCommand:
498 except error.UnknownCommand:
499 pass
499 pass
500 raise error.Abort(self.badalias, hint=hint)
500 raise error.Abort(self.badalias, hint=hint)
501 if self.shadows:
501 if self.shadows:
502 ui.debug("alias '%s' shadows command '%s'\n" %
502 ui.debug("alias '%s' shadows command '%s'\n" %
503 (self.name, self.cmdname))
503 (self.name, self.cmdname))
504
504
505 ui.log('commandalias', "alias '%s' expands to '%s'\n",
505 ui.log('commandalias', "alias '%s' expands to '%s'\n",
506 self.name, self.definition)
506 self.name, self.definition)
507 if util.safehasattr(self, 'shell'):
507 if util.safehasattr(self, 'shell'):
508 return self.fn(ui, *args, **opts)
508 return self.fn(ui, *args, **opts)
509 else:
509 else:
510 try:
510 try:
511 return util.checksignature(self.fn)(ui, *args, **opts)
511 return util.checksignature(self.fn)(ui, *args, **opts)
512 except error.SignatureError:
512 except error.SignatureError:
513 args = ' '.join([self.cmdname] + self.args)
513 args = ' '.join([self.cmdname] + self.args)
514 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
514 ui.debug("alias '%s' expands to '%s'\n" % (self.name, args))
515 raise
515 raise
516
516
517 def addaliases(ui, cmdtable):
517 def addaliases(ui, cmdtable):
518 # aliases are processed after extensions have been loaded, so they
518 # aliases are processed after extensions have been loaded, so they
519 # may use extension commands. Aliases can also use other alias definitions,
519 # may use extension commands. Aliases can also use other alias definitions,
520 # but only if they have been defined prior to the current definition.
520 # but only if they have been defined prior to the current definition.
521 for alias, definition in ui.configitems('alias'):
521 for alias, definition in ui.configitems('alias'):
522 source = ui.configsource('alias', alias)
522 source = ui.configsource('alias', alias)
523 aliasdef = cmdalias(alias, definition, cmdtable, source)
523 aliasdef = cmdalias(alias, definition, cmdtable, source)
524
524
525 try:
525 try:
526 olddef = cmdtable[aliasdef.cmd][0]
526 olddef = cmdtable[aliasdef.cmd][0]
527 if olddef.definition == aliasdef.definition:
527 if olddef.definition == aliasdef.definition:
528 continue
528 continue
529 except (KeyError, AttributeError):
529 except (KeyError, AttributeError):
530 # definition might not exist or it might not be a cmdalias
530 # definition might not exist or it might not be a cmdalias
531 pass
531 pass
532
532
533 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
533 cmdtable[aliasdef.name] = (aliasdef, aliasdef.opts, aliasdef.help)
534
534
535 def _parse(ui, args):
535 def _parse(ui, args):
536 options = {}
536 options = {}
537 cmdoptions = {}
537 cmdoptions = {}
538
538
539 try:
539 try:
540 args = fancyopts.fancyopts(args, commands.globalopts, options)
540 args = fancyopts.fancyopts(args, commands.globalopts, options)
541 except fancyopts.getopt.GetoptError as inst:
541 except fancyopts.getopt.GetoptError as inst:
542 raise error.CommandError(None, inst)
542 raise error.CommandError(None, inst)
543
543
544 if args:
544 if args:
545 cmd, args = args[0], args[1:]
545 cmd, args = args[0], args[1:]
546 aliases, entry = cmdutil.findcmd(cmd, commands.table,
546 aliases, entry = cmdutil.findcmd(cmd, commands.table,
547 ui.configbool("ui", "strict"))
547 ui.configbool("ui", "strict"))
548 cmd = aliases[0]
548 cmd = aliases[0]
549 args = aliasargs(entry[0], args)
549 args = aliasargs(entry[0], args)
550 defaults = ui.config("defaults", cmd)
550 defaults = ui.config("defaults", cmd)
551 if defaults:
551 if defaults:
552 args = map(util.expandpath, shlex.split(defaults)) + args
552 args = map(util.expandpath, shlex.split(defaults)) + args
553 c = list(entry[1])
553 c = list(entry[1])
554 else:
554 else:
555 cmd = None
555 cmd = None
556 c = []
556 c = []
557
557
558 # combine global options into local
558 # combine global options into local
559 for o in commands.globalopts:
559 for o in commands.globalopts:
560 c.append((o[0], o[1], options[o[1]], o[3]))
560 c.append((o[0], o[1], options[o[1]], o[3]))
561
561
562 try:
562 try:
563 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
563 args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True)
564 except fancyopts.getopt.GetoptError as inst:
564 except fancyopts.getopt.GetoptError as inst:
565 raise error.CommandError(cmd, inst)
565 raise error.CommandError(cmd, inst)
566
566
567 # separate global options back out
567 # separate global options back out
568 for o in commands.globalopts:
568 for o in commands.globalopts:
569 n = o[1]
569 n = o[1]
570 options[n] = cmdoptions[n]
570 options[n] = cmdoptions[n]
571 del cmdoptions[n]
571 del cmdoptions[n]
572
572
573 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
573 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
574
574
575 def _parseconfig(ui, config):
575 def _parseconfig(ui, config):
576 """parse the --config options from the command line"""
576 """parse the --config options from the command line"""
577 configs = []
577 configs = []
578
578
579 for cfg in config:
579 for cfg in config:
580 try:
580 try:
581 name, value = [cfgelem.strip()
581 name, value = [cfgelem.strip()
582 for cfgelem in cfg.split('=', 1)]
582 for cfgelem in cfg.split('=', 1)]
583 section, name = name.split('.', 1)
583 section, name = name.split('.', 1)
584 if not section or not name:
584 if not section or not name:
585 raise IndexError
585 raise IndexError
586 ui.setconfig(section, name, value, '--config')
586 ui.setconfig(section, name, value, '--config')
587 configs.append((section, name, value))
587 configs.append((section, name, value))
588 except (IndexError, ValueError):
588 except (IndexError, ValueError):
589 raise error.Abort(_('malformed --config option: %r '
589 raise error.Abort(_('malformed --config option: %r '
590 '(use --config section.name=value)') % cfg)
590 '(use --config section.name=value)') % cfg)
591
591
592 return configs
592 return configs
593
593
594 def _earlygetopt(aliases, args):
594 def _earlygetopt(aliases, args):
595 """Return list of values for an option (or aliases).
595 """Return list of values for an option (or aliases).
596
596
597 The values are listed in the order they appear in args.
597 The values are listed in the order they appear in args.
598 The options and values are removed from args.
598 The options and values are removed from args.
599
599
600 >>> args = ['x', '--cwd', 'foo', 'y']
600 >>> args = ['x', '--cwd', 'foo', 'y']
601 >>> _earlygetopt(['--cwd'], args), args
601 >>> _earlygetopt(['--cwd'], args), args
602 (['foo'], ['x', 'y'])
602 (['foo'], ['x', 'y'])
603
603
604 >>> args = ['x', '--cwd=bar', 'y']
604 >>> args = ['x', '--cwd=bar', 'y']
605 >>> _earlygetopt(['--cwd'], args), args
605 >>> _earlygetopt(['--cwd'], args), args
606 (['bar'], ['x', 'y'])
606 (['bar'], ['x', 'y'])
607
607
608 >>> args = ['x', '-R', 'foo', 'y']
608 >>> args = ['x', '-R', 'foo', 'y']
609 >>> _earlygetopt(['-R'], args), args
609 >>> _earlygetopt(['-R'], args), args
610 (['foo'], ['x', 'y'])
610 (['foo'], ['x', 'y'])
611
611
612 >>> args = ['x', '-Rbar', 'y']
612 >>> args = ['x', '-Rbar', 'y']
613 >>> _earlygetopt(['-R'], args), args
613 >>> _earlygetopt(['-R'], args), args
614 (['bar'], ['x', 'y'])
614 (['bar'], ['x', 'y'])
615 """
615 """
616 try:
616 try:
617 argcount = args.index("--")
617 argcount = args.index("--")
618 except ValueError:
618 except ValueError:
619 argcount = len(args)
619 argcount = len(args)
620 shortopts = [opt for opt in aliases if len(opt) == 2]
620 shortopts = [opt for opt in aliases if len(opt) == 2]
621 values = []
621 values = []
622 pos = 0
622 pos = 0
623 while pos < argcount:
623 while pos < argcount:
624 fullarg = arg = args[pos]
624 fullarg = arg = args[pos]
625 equals = arg.find('=')
625 equals = arg.find('=')
626 if equals > -1:
626 if equals > -1:
627 arg = arg[:equals]
627 arg = arg[:equals]
628 if arg in aliases:
628 if arg in aliases:
629 del args[pos]
629 del args[pos]
630 if equals > -1:
630 if equals > -1:
631 values.append(fullarg[equals + 1:])
631 values.append(fullarg[equals + 1:])
632 argcount -= 1
632 argcount -= 1
633 else:
633 else:
634 if pos + 1 >= argcount:
634 if pos + 1 >= argcount:
635 # ignore and let getopt report an error if there is no value
635 # ignore and let getopt report an error if there is no value
636 break
636 break
637 values.append(args.pop(pos))
637 values.append(args.pop(pos))
638 argcount -= 2
638 argcount -= 2
639 elif arg[:2] in shortopts:
639 elif arg[:2] in shortopts:
640 # short option can have no following space, e.g. hg log -Rfoo
640 # short option can have no following space, e.g. hg log -Rfoo
641 values.append(args.pop(pos)[2:])
641 values.append(args.pop(pos)[2:])
642 argcount -= 1
642 argcount -= 1
643 else:
643 else:
644 pos += 1
644 pos += 1
645 return values
645 return values
646
646
647 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
647 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
648 # run pre-hook, and abort if it fails
648 # run pre-hook, and abort if it fails
649 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
649 hook.hook(lui, repo, "pre-%s" % cmd, True, args=" ".join(fullargs),
650 pats=cmdpats, opts=cmdoptions)
650 pats=cmdpats, opts=cmdoptions)
651 try:
651 try:
652 ret = _runcommand(ui, options, cmd, d)
652 ret = _runcommand(ui, options, cmd, d)
653 # run post-hook, passing command result
653 # run post-hook, passing command result
654 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
654 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
655 result=ret, pats=cmdpats, opts=cmdoptions)
655 result=ret, pats=cmdpats, opts=cmdoptions)
656 except Exception:
656 except Exception:
657 # run failure hook and re-raise
657 # run failure hook and re-raise
658 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
658 hook.hook(lui, repo, "fail-%s" % cmd, False, args=" ".join(fullargs),
659 pats=cmdpats, opts=cmdoptions)
659 pats=cmdpats, opts=cmdoptions)
660 raise
660 raise
661 return ret
661 return ret
662
662
663 def _getlocal(ui, rpath, wd=None):
663 def _getlocal(ui, rpath, wd=None):
664 """Return (path, local ui object) for the given target path.
664 """Return (path, local ui object) for the given target path.
665
665
666 Takes paths in [cwd]/.hg/hgrc into account."
666 Takes paths in [cwd]/.hg/hgrc into account."
667 """
667 """
668 if wd is None:
668 if wd is None:
669 try:
669 try:
670 wd = os.getcwd()
670 wd = pycompat.getcwd()
671 except OSError as e:
671 except OSError as e:
672 raise error.Abort(_("error getting current working directory: %s") %
672 raise error.Abort(_("error getting current working directory: %s") %
673 e.strerror)
673 e.strerror)
674 path = cmdutil.findrepo(wd) or ""
674 path = cmdutil.findrepo(wd) or ""
675 if not path:
675 if not path:
676 lui = ui
676 lui = ui
677 else:
677 else:
678 lui = ui.copy()
678 lui = ui.copy()
679 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
679 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
680
680
681 if rpath and rpath[-1]:
681 if rpath and rpath[-1]:
682 path = lui.expandpath(rpath[-1])
682 path = lui.expandpath(rpath[-1])
683 lui = ui.copy()
683 lui = ui.copy()
684 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
684 lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
685
685
686 return path, lui
686 return path, lui
687
687
688 def _checkshellalias(lui, ui, args):
688 def _checkshellalias(lui, ui, args):
689 """Return the function to run the shell alias, if it is required"""
689 """Return the function to run the shell alias, if it is required"""
690 options = {}
690 options = {}
691
691
692 try:
692 try:
693 args = fancyopts.fancyopts(args, commands.globalopts, options)
693 args = fancyopts.fancyopts(args, commands.globalopts, options)
694 except fancyopts.getopt.GetoptError:
694 except fancyopts.getopt.GetoptError:
695 return
695 return
696
696
697 if not args:
697 if not args:
698 return
698 return
699
699
700 cmdtable = commands.table
700 cmdtable = commands.table
701
701
702 cmd = args[0]
702 cmd = args[0]
703 try:
703 try:
704 strict = ui.configbool("ui", "strict")
704 strict = ui.configbool("ui", "strict")
705 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
705 aliases, entry = cmdutil.findcmd(cmd, cmdtable, strict)
706 except (error.AmbiguousCommand, error.UnknownCommand):
706 except (error.AmbiguousCommand, error.UnknownCommand):
707 return
707 return
708
708
709 cmd = aliases[0]
709 cmd = aliases[0]
710 fn = entry[0]
710 fn = entry[0]
711
711
712 if cmd and util.safehasattr(fn, 'shell'):
712 if cmd and util.safehasattr(fn, 'shell'):
713 d = lambda: fn(ui, *args[1:])
713 d = lambda: fn(ui, *args[1:])
714 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
714 return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d,
715 [], {})
715 [], {})
716
716
717 _loaded = set()
717 _loaded = set()
718
718
719 # list of (objname, loadermod, loadername) tuple:
719 # list of (objname, loadermod, loadername) tuple:
720 # - objname is the name of an object in extension module, from which
720 # - objname is the name of an object in extension module, from which
721 # extra information is loaded
721 # extra information is loaded
722 # - loadermod is the module where loader is placed
722 # - loadermod is the module where loader is placed
723 # - loadername is the name of the function, which takes (ui, extensionname,
723 # - loadername is the name of the function, which takes (ui, extensionname,
724 # extraobj) arguments
724 # extraobj) arguments
725 extraloaders = [
725 extraloaders = [
726 ('cmdtable', commands, 'loadcmdtable'),
726 ('cmdtable', commands, 'loadcmdtable'),
727 ('filesetpredicate', fileset, 'loadpredicate'),
727 ('filesetpredicate', fileset, 'loadpredicate'),
728 ('revsetpredicate', revset, 'loadpredicate'),
728 ('revsetpredicate', revset, 'loadpredicate'),
729 ('templatefilter', templatefilters, 'loadfilter'),
729 ('templatefilter', templatefilters, 'loadfilter'),
730 ('templatefunc', templater, 'loadfunction'),
730 ('templatefunc', templater, 'loadfunction'),
731 ('templatekeyword', templatekw, 'loadkeyword'),
731 ('templatekeyword', templatekw, 'loadkeyword'),
732 ]
732 ]
733
733
734 def _dispatch(req):
734 def _dispatch(req):
735 args = req.args
735 args = req.args
736 ui = req.ui
736 ui = req.ui
737
737
738 # check for cwd
738 # check for cwd
739 cwd = _earlygetopt(['--cwd'], args)
739 cwd = _earlygetopt(['--cwd'], args)
740 if cwd:
740 if cwd:
741 os.chdir(cwd[-1])
741 os.chdir(cwd[-1])
742
742
743 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
743 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
744 path, lui = _getlocal(ui, rpath)
744 path, lui = _getlocal(ui, rpath)
745
745
746 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
746 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
747 # reposetup. Programs like TortoiseHg will call _dispatch several
747 # reposetup. Programs like TortoiseHg will call _dispatch several
748 # times so we keep track of configured extensions in _loaded.
748 # times so we keep track of configured extensions in _loaded.
749 extensions.loadall(lui)
749 extensions.loadall(lui)
750 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
750 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
751 # Propagate any changes to lui.__class__ by extensions
751 # Propagate any changes to lui.__class__ by extensions
752 ui.__class__ = lui.__class__
752 ui.__class__ = lui.__class__
753
753
754 # (uisetup and extsetup are handled in extensions.loadall)
754 # (uisetup and extsetup are handled in extensions.loadall)
755
755
756 for name, module in exts:
756 for name, module in exts:
757 for objname, loadermod, loadername in extraloaders:
757 for objname, loadermod, loadername in extraloaders:
758 extraobj = getattr(module, objname, None)
758 extraobj = getattr(module, objname, None)
759 if extraobj is not None:
759 if extraobj is not None:
760 getattr(loadermod, loadername)(ui, name, extraobj)
760 getattr(loadermod, loadername)(ui, name, extraobj)
761 _loaded.add(name)
761 _loaded.add(name)
762
762
763 # (reposetup is handled in hg.repository)
763 # (reposetup is handled in hg.repository)
764
764
765 # Side-effect of accessing is debugcommands module is guaranteed to be
765 # Side-effect of accessing is debugcommands module is guaranteed to be
766 # imported and commands.table is populated.
766 # imported and commands.table is populated.
767 debugcommands.command
767 debugcommands.command
768
768
769 addaliases(lui, commands.table)
769 addaliases(lui, commands.table)
770
770
771 # All aliases and commands are completely defined, now.
771 # All aliases and commands are completely defined, now.
772 # Check abbreviation/ambiguity of shell alias.
772 # Check abbreviation/ambiguity of shell alias.
773 shellaliasfn = _checkshellalias(lui, ui, args)
773 shellaliasfn = _checkshellalias(lui, ui, args)
774 if shellaliasfn:
774 if shellaliasfn:
775 with profiling.maybeprofile(lui):
775 with profiling.maybeprofile(lui):
776 return shellaliasfn()
776 return shellaliasfn()
777
777
778 # check for fallback encoding
778 # check for fallback encoding
779 fallback = lui.config('ui', 'fallbackencoding')
779 fallback = lui.config('ui', 'fallbackencoding')
780 if fallback:
780 if fallback:
781 encoding.fallbackencoding = fallback
781 encoding.fallbackencoding = fallback
782
782
783 fullargs = args
783 fullargs = args
784 cmd, func, args, options, cmdoptions = _parse(lui, args)
784 cmd, func, args, options, cmdoptions = _parse(lui, args)
785
785
786 if options["config"]:
786 if options["config"]:
787 raise error.Abort(_("option --config may not be abbreviated!"))
787 raise error.Abort(_("option --config may not be abbreviated!"))
788 if options["cwd"]:
788 if options["cwd"]:
789 raise error.Abort(_("option --cwd may not be abbreviated!"))
789 raise error.Abort(_("option --cwd may not be abbreviated!"))
790 if options["repository"]:
790 if options["repository"]:
791 raise error.Abort(_(
791 raise error.Abort(_(
792 "option -R has to be separated from other options (e.g. not -qR) "
792 "option -R has to be separated from other options (e.g. not -qR) "
793 "and --repository may only be abbreviated as --repo!"))
793 "and --repository may only be abbreviated as --repo!"))
794
794
795 if options["encoding"]:
795 if options["encoding"]:
796 encoding.encoding = options["encoding"]
796 encoding.encoding = options["encoding"]
797 if options["encodingmode"]:
797 if options["encodingmode"]:
798 encoding.encodingmode = options["encodingmode"]
798 encoding.encodingmode = options["encodingmode"]
799 if options["time"]:
799 if options["time"]:
800 def get_times():
800 def get_times():
801 t = os.times()
801 t = os.times()
802 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
802 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
803 t = (t[0], t[1], t[2], t[3], time.clock())
803 t = (t[0], t[1], t[2], t[3], time.clock())
804 return t
804 return t
805 s = get_times()
805 s = get_times()
806 def print_time():
806 def print_time():
807 t = get_times()
807 t = get_times()
808 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
808 ui.warn(_("time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
809 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
809 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
810 atexit.register(print_time)
810 atexit.register(print_time)
811
811
812 uis = set([ui, lui])
812 uis = set([ui, lui])
813
813
814 if req.repo:
814 if req.repo:
815 uis.add(req.repo.ui)
815 uis.add(req.repo.ui)
816
816
817 if options['verbose'] or options['debug'] or options['quiet']:
817 if options['verbose'] or options['debug'] or options['quiet']:
818 for opt in ('verbose', 'debug', 'quiet'):
818 for opt in ('verbose', 'debug', 'quiet'):
819 val = str(bool(options[opt]))
819 val = str(bool(options[opt]))
820 for ui_ in uis:
820 for ui_ in uis:
821 ui_.setconfig('ui', opt, val, '--' + opt)
821 ui_.setconfig('ui', opt, val, '--' + opt)
822
822
823 if options['profile']:
823 if options['profile']:
824 for ui_ in uis:
824 for ui_ in uis:
825 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
825 ui_.setconfig('profiling', 'enabled', 'true', '--profile')
826
826
827 if options['traceback']:
827 if options['traceback']:
828 for ui_ in uis:
828 for ui_ in uis:
829 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
829 ui_.setconfig('ui', 'traceback', 'on', '--traceback')
830
830
831 if options['noninteractive']:
831 if options['noninteractive']:
832 for ui_ in uis:
832 for ui_ in uis:
833 ui_.setconfig('ui', 'interactive', 'off', '-y')
833 ui_.setconfig('ui', 'interactive', 'off', '-y')
834
834
835 if cmdoptions.get('insecure', False):
835 if cmdoptions.get('insecure', False):
836 for ui_ in uis:
836 for ui_ in uis:
837 ui_.insecureconnections = True
837 ui_.insecureconnections = True
838
838
839 if options['version']:
839 if options['version']:
840 return commands.version_(ui)
840 return commands.version_(ui)
841 if options['help']:
841 if options['help']:
842 return commands.help_(ui, cmd, command=cmd is not None)
842 return commands.help_(ui, cmd, command=cmd is not None)
843 elif not cmd:
843 elif not cmd:
844 return commands.help_(ui, 'shortlist')
844 return commands.help_(ui, 'shortlist')
845
845
846 with profiling.maybeprofile(lui):
846 with profiling.maybeprofile(lui):
847 repo = None
847 repo = None
848 cmdpats = args[:]
848 cmdpats = args[:]
849 if not func.norepo:
849 if not func.norepo:
850 # use the repo from the request only if we don't have -R
850 # use the repo from the request only if we don't have -R
851 if not rpath and not cwd:
851 if not rpath and not cwd:
852 repo = req.repo
852 repo = req.repo
853
853
854 if repo:
854 if repo:
855 # set the descriptors of the repo ui to those of ui
855 # set the descriptors of the repo ui to those of ui
856 repo.ui.fin = ui.fin
856 repo.ui.fin = ui.fin
857 repo.ui.fout = ui.fout
857 repo.ui.fout = ui.fout
858 repo.ui.ferr = ui.ferr
858 repo.ui.ferr = ui.ferr
859 else:
859 else:
860 try:
860 try:
861 repo = hg.repository(ui, path=path)
861 repo = hg.repository(ui, path=path)
862 if not repo.local():
862 if not repo.local():
863 raise error.Abort(_("repository '%s' is not local")
863 raise error.Abort(_("repository '%s' is not local")
864 % path)
864 % path)
865 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
865 repo.ui.setconfig("bundle", "mainreporoot", repo.root,
866 'repo')
866 'repo')
867 except error.RequirementError:
867 except error.RequirementError:
868 raise
868 raise
869 except error.RepoError:
869 except error.RepoError:
870 if rpath and rpath[-1]: # invalid -R path
870 if rpath and rpath[-1]: # invalid -R path
871 raise
871 raise
872 if not func.optionalrepo:
872 if not func.optionalrepo:
873 if func.inferrepo and args and not path:
873 if func.inferrepo and args and not path:
874 # try to infer -R from command args
874 # try to infer -R from command args
875 repos = map(cmdutil.findrepo, args)
875 repos = map(cmdutil.findrepo, args)
876 guess = repos[0]
876 guess = repos[0]
877 if guess and repos.count(guess) == len(repos):
877 if guess and repos.count(guess) == len(repos):
878 req.args = ['--repository', guess] + fullargs
878 req.args = ['--repository', guess] + fullargs
879 return _dispatch(req)
879 return _dispatch(req)
880 if not path:
880 if not path:
881 raise error.RepoError(_("no repository found in"
881 raise error.RepoError(_("no repository found in"
882 " '%s' (.hg not found)")
882 " '%s' (.hg not found)")
883 % os.getcwd())
883 % os.getcwd())
884 raise
884 raise
885 if repo:
885 if repo:
886 ui = repo.ui
886 ui = repo.ui
887 if options['hidden']:
887 if options['hidden']:
888 repo = repo.unfiltered()
888 repo = repo.unfiltered()
889 args.insert(0, repo)
889 args.insert(0, repo)
890 elif rpath:
890 elif rpath:
891 ui.warn(_("warning: --repository ignored\n"))
891 ui.warn(_("warning: --repository ignored\n"))
892
892
893 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
893 msg = ' '.join(' ' in a and repr(a) or a for a in fullargs)
894 ui.log("command", '%s\n', msg)
894 ui.log("command", '%s\n', msg)
895 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
895 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
896 try:
896 try:
897 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
897 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
898 cmdpats, cmdoptions)
898 cmdpats, cmdoptions)
899 finally:
899 finally:
900 if repo and repo != req.repo:
900 if repo and repo != req.repo:
901 repo.close()
901 repo.close()
902
902
903 def _runcommand(ui, options, cmd, cmdfunc):
903 def _runcommand(ui, options, cmd, cmdfunc):
904 """Run a command function, possibly with profiling enabled."""
904 """Run a command function, possibly with profiling enabled."""
905 try:
905 try:
906 return cmdfunc()
906 return cmdfunc()
907 except error.SignatureError:
907 except error.SignatureError:
908 raise error.CommandError(cmd, _('invalid arguments'))
908 raise error.CommandError(cmd, _('invalid arguments'))
909
909
910 def _exceptionwarning(ui):
910 def _exceptionwarning(ui):
911 """Produce a warning message for the current active exception"""
911 """Produce a warning message for the current active exception"""
912
912
913 # For compatibility checking, we discard the portion of the hg
913 # For compatibility checking, we discard the portion of the hg
914 # version after the + on the assumption that if a "normal
914 # version after the + on the assumption that if a "normal
915 # user" is running a build with a + in it the packager
915 # user" is running a build with a + in it the packager
916 # probably built from fairly close to a tag and anyone with a
916 # probably built from fairly close to a tag and anyone with a
917 # 'make local' copy of hg (where the version number can be out
917 # 'make local' copy of hg (where the version number can be out
918 # of date) will be clueful enough to notice the implausible
918 # of date) will be clueful enough to notice the implausible
919 # version number and try updating.
919 # version number and try updating.
920 ct = util.versiontuple(n=2)
920 ct = util.versiontuple(n=2)
921 worst = None, ct, ''
921 worst = None, ct, ''
922 if ui.config('ui', 'supportcontact', None) is None:
922 if ui.config('ui', 'supportcontact', None) is None:
923 for name, mod in extensions.extensions():
923 for name, mod in extensions.extensions():
924 testedwith = getattr(mod, 'testedwith', '')
924 testedwith = getattr(mod, 'testedwith', '')
925 report = getattr(mod, 'buglink', _('the extension author.'))
925 report = getattr(mod, 'buglink', _('the extension author.'))
926 if not testedwith.strip():
926 if not testedwith.strip():
927 # We found an untested extension. It's likely the culprit.
927 # We found an untested extension. It's likely the culprit.
928 worst = name, 'unknown', report
928 worst = name, 'unknown', report
929 break
929 break
930
930
931 # Never blame on extensions bundled with Mercurial.
931 # Never blame on extensions bundled with Mercurial.
932 if extensions.ismoduleinternal(mod):
932 if extensions.ismoduleinternal(mod):
933 continue
933 continue
934
934
935 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
935 tested = [util.versiontuple(t, 2) for t in testedwith.split()]
936 if ct in tested:
936 if ct in tested:
937 continue
937 continue
938
938
939 lower = [t for t in tested if t < ct]
939 lower = [t for t in tested if t < ct]
940 nearest = max(lower or tested)
940 nearest = max(lower or tested)
941 if worst[0] is None or nearest < worst[1]:
941 if worst[0] is None or nearest < worst[1]:
942 worst = name, nearest, report
942 worst = name, nearest, report
943 if worst[0] is not None:
943 if worst[0] is not None:
944 name, testedwith, report = worst
944 name, testedwith, report = worst
945 if not isinstance(testedwith, str):
945 if not isinstance(testedwith, str):
946 testedwith = '.'.join([str(c) for c in testedwith])
946 testedwith = '.'.join([str(c) for c in testedwith])
947 warning = (_('** Unknown exception encountered with '
947 warning = (_('** Unknown exception encountered with '
948 'possibly-broken third-party extension %s\n'
948 'possibly-broken third-party extension %s\n'
949 '** which supports versions %s of Mercurial.\n'
949 '** which supports versions %s of Mercurial.\n'
950 '** Please disable %s and try your action again.\n'
950 '** Please disable %s and try your action again.\n'
951 '** If that fixes the bug please report it to %s\n')
951 '** If that fixes the bug please report it to %s\n')
952 % (name, testedwith, name, report))
952 % (name, testedwith, name, report))
953 else:
953 else:
954 bugtracker = ui.config('ui', 'supportcontact', None)
954 bugtracker = ui.config('ui', 'supportcontact', None)
955 if bugtracker is None:
955 if bugtracker is None:
956 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
956 bugtracker = _("https://mercurial-scm.org/wiki/BugTracker")
957 warning = (_("** unknown exception encountered, "
957 warning = (_("** unknown exception encountered, "
958 "please report by visiting\n** ") + bugtracker + '\n')
958 "please report by visiting\n** ") + bugtracker + '\n')
959 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
959 warning += ((_("** Python %s\n") % sys.version.replace('\n', '')) +
960 (_("** Mercurial Distributed SCM (version %s)\n") %
960 (_("** Mercurial Distributed SCM (version %s)\n") %
961 util.version()) +
961 util.version()) +
962 (_("** Extensions loaded: %s\n") %
962 (_("** Extensions loaded: %s\n") %
963 ", ".join([x[0] for x in extensions.extensions()])))
963 ", ".join([x[0] for x in extensions.extensions()])))
964 return warning
964 return warning
965
965
966 def handlecommandexception(ui):
966 def handlecommandexception(ui):
967 """Produce a warning message for broken commands
967 """Produce a warning message for broken commands
968
968
969 Called when handling an exception; the exception is reraised if
969 Called when handling an exception; the exception is reraised if
970 this function returns False, ignored otherwise.
970 this function returns False, ignored otherwise.
971 """
971 """
972 warning = _exceptionwarning(ui)
972 warning = _exceptionwarning(ui)
973 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
973 ui.log("commandexception", "%s\n%s\n", warning, traceback.format_exc())
974 ui.warn(warning)
974 ui.warn(warning)
975 return False # re-raise the exception
975 return False # re-raise the exception
@@ -1,231 +1,235 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 # os.getcwd() on Python 3 returns string, but it has os.getcwdb() which
47 # returns bytes.
48 getcwd = os.getcwdb
46
49
47 # TODO: .buffer might not exist if std streams were replaced; we'll need
50 # TODO: .buffer might not exist if std streams were replaced; we'll need
48 # a silly wrapper to make a bytes stream backed by a unicode one.
51 # a silly wrapper to make a bytes stream backed by a unicode one.
49 stdin = sys.stdin.buffer
52 stdin = sys.stdin.buffer
50 stdout = sys.stdout.buffer
53 stdout = sys.stdout.buffer
51 stderr = sys.stderr.buffer
54 stderr = sys.stderr.buffer
52
55
53 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
56 # Since Python 3 converts argv to wchar_t type by Py_DecodeLocale() on Unix,
54 # we can use os.fsencode() to get back bytes argv.
57 # we can use os.fsencode() to get back bytes argv.
55 #
58 #
56 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
59 # https://hg.python.org/cpython/file/v3.5.1/Programs/python.c#l55
57 #
60 #
58 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
61 # TODO: On Windows, the native argv is wchar_t, so we'll need a different
59 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
62 # workaround to simulate the Python 2 (i.e. ANSI Win32 API) behavior.
60 sysargv = list(map(os.fsencode, sys.argv))
63 sysargv = list(map(os.fsencode, sys.argv))
61
64
62 def sysstr(s):
65 def sysstr(s):
63 """Return a keyword str to be passed to Python functions such as
66 """Return a keyword str to be passed to Python functions such as
64 getattr() and str.encode()
67 getattr() and str.encode()
65
68
66 This never raises UnicodeDecodeError. Non-ascii characters are
69 This never raises UnicodeDecodeError. Non-ascii characters are
67 considered invalid and mapped to arbitrary but unique code points
70 considered invalid and mapped to arbitrary but unique code points
68 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
71 such that 'sysstr(a) != sysstr(b)' for all 'a != b'.
69 """
72 """
70 if isinstance(s, builtins.str):
73 if isinstance(s, builtins.str):
71 return s
74 return s
72 return s.decode(u'latin-1')
75 return s.decode(u'latin-1')
73
76
74 def _wrapattrfunc(f):
77 def _wrapattrfunc(f):
75 @functools.wraps(f)
78 @functools.wraps(f)
76 def w(object, name, *args):
79 def w(object, name, *args):
77 return f(object, sysstr(name), *args)
80 return f(object, sysstr(name), *args)
78 return w
81 return w
79
82
80 # these wrappers are automagically imported by hgloader
83 # these wrappers are automagically imported by hgloader
81 delattr = _wrapattrfunc(builtins.delattr)
84 delattr = _wrapattrfunc(builtins.delattr)
82 getattr = _wrapattrfunc(builtins.getattr)
85 getattr = _wrapattrfunc(builtins.getattr)
83 hasattr = _wrapattrfunc(builtins.hasattr)
86 hasattr = _wrapattrfunc(builtins.hasattr)
84 setattr = _wrapattrfunc(builtins.setattr)
87 setattr = _wrapattrfunc(builtins.setattr)
85 xrange = builtins.range
88 xrange = builtins.range
86
89
87 else:
90 else:
88 def sysstr(s):
91 def sysstr(s):
89 return s
92 return s
90
93
91 # Partial backport from os.py in Python 3, which only accepts bytes.
94 # Partial backport from os.py in Python 3, which only accepts bytes.
92 # In Python 2, our paths should only ever be bytes, a unicode path
95 # In Python 2, our paths should only ever be bytes, a unicode path
93 # indicates a bug.
96 # indicates a bug.
94 def fsencode(filename):
97 def fsencode(filename):
95 if isinstance(filename, str):
98 if isinstance(filename, str):
96 return filename
99 return filename
97 else:
100 else:
98 raise TypeError(
101 raise TypeError(
99 "expect str, not %s" % type(filename).__name__)
102 "expect str, not %s" % type(filename).__name__)
100
103
101 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
104 # In Python 2, fsdecode() has a very chance to receive bytes. So it's
102 # better not to touch Python 2 part as it's already working fine.
105 # better not to touch Python 2 part as it's already working fine.
103 def fsdecode(filename):
106 def fsdecode(filename):
104 return filename
107 return filename
105
108
106 osname = os.name
109 osname = os.name
107 ospathsep = os.pathsep
110 ospathsep = os.pathsep
108 ossep = os.sep
111 ossep = os.sep
109 stdin = sys.stdin
112 stdin = sys.stdin
110 stdout = sys.stdout
113 stdout = sys.stdout
111 stderr = sys.stderr
114 stderr = sys.stderr
112 sysargv = sys.argv
115 sysargv = sys.argv
116 getcwd = os.getcwd
113
117
114 stringio = io.StringIO
118 stringio = io.StringIO
115 empty = _queue.Empty
119 empty = _queue.Empty
116 queue = _queue.Queue
120 queue = _queue.Queue
117
121
118 class _pycompatstub(object):
122 class _pycompatstub(object):
119 def __init__(self):
123 def __init__(self):
120 self._aliases = {}
124 self._aliases = {}
121
125
122 def _registeraliases(self, origin, items):
126 def _registeraliases(self, origin, items):
123 """Add items that will be populated at the first access"""
127 """Add items that will be populated at the first access"""
124 items = map(sysstr, items)
128 items = map(sysstr, items)
125 self._aliases.update(
129 self._aliases.update(
126 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
130 (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item))
127 for item in items)
131 for item in items)
128
132
129 def __getattr__(self, name):
133 def __getattr__(self, name):
130 try:
134 try:
131 origin, item = self._aliases[name]
135 origin, item = self._aliases[name]
132 except KeyError:
136 except KeyError:
133 raise AttributeError(name)
137 raise AttributeError(name)
134 self.__dict__[name] = obj = getattr(origin, item)
138 self.__dict__[name] = obj = getattr(origin, item)
135 return obj
139 return obj
136
140
137 httpserver = _pycompatstub()
141 httpserver = _pycompatstub()
138 urlreq = _pycompatstub()
142 urlreq = _pycompatstub()
139 urlerr = _pycompatstub()
143 urlerr = _pycompatstub()
140 if not ispy3:
144 if not ispy3:
141 import BaseHTTPServer
145 import BaseHTTPServer
142 import CGIHTTPServer
146 import CGIHTTPServer
143 import SimpleHTTPServer
147 import SimpleHTTPServer
144 import urllib2
148 import urllib2
145 import urllib
149 import urllib
146 urlreq._registeraliases(urllib, (
150 urlreq._registeraliases(urllib, (
147 "addclosehook",
151 "addclosehook",
148 "addinfourl",
152 "addinfourl",
149 "ftpwrapper",
153 "ftpwrapper",
150 "pathname2url",
154 "pathname2url",
151 "quote",
155 "quote",
152 "splitattr",
156 "splitattr",
153 "splitpasswd",
157 "splitpasswd",
154 "splitport",
158 "splitport",
155 "splituser",
159 "splituser",
156 "unquote",
160 "unquote",
157 "url2pathname",
161 "url2pathname",
158 "urlencode",
162 "urlencode",
159 ))
163 ))
160 urlreq._registeraliases(urllib2, (
164 urlreq._registeraliases(urllib2, (
161 "AbstractHTTPHandler",
165 "AbstractHTTPHandler",
162 "BaseHandler",
166 "BaseHandler",
163 "build_opener",
167 "build_opener",
164 "FileHandler",
168 "FileHandler",
165 "FTPHandler",
169 "FTPHandler",
166 "HTTPBasicAuthHandler",
170 "HTTPBasicAuthHandler",
167 "HTTPDigestAuthHandler",
171 "HTTPDigestAuthHandler",
168 "HTTPHandler",
172 "HTTPHandler",
169 "HTTPPasswordMgrWithDefaultRealm",
173 "HTTPPasswordMgrWithDefaultRealm",
170 "HTTPSHandler",
174 "HTTPSHandler",
171 "install_opener",
175 "install_opener",
172 "ProxyHandler",
176 "ProxyHandler",
173 "Request",
177 "Request",
174 "urlopen",
178 "urlopen",
175 ))
179 ))
176 urlerr._registeraliases(urllib2, (
180 urlerr._registeraliases(urllib2, (
177 "HTTPError",
181 "HTTPError",
178 "URLError",
182 "URLError",
179 ))
183 ))
180 httpserver._registeraliases(BaseHTTPServer, (
184 httpserver._registeraliases(BaseHTTPServer, (
181 "HTTPServer",
185 "HTTPServer",
182 "BaseHTTPRequestHandler",
186 "BaseHTTPRequestHandler",
183 ))
187 ))
184 httpserver._registeraliases(SimpleHTTPServer, (
188 httpserver._registeraliases(SimpleHTTPServer, (
185 "SimpleHTTPRequestHandler",
189 "SimpleHTTPRequestHandler",
186 ))
190 ))
187 httpserver._registeraliases(CGIHTTPServer, (
191 httpserver._registeraliases(CGIHTTPServer, (
188 "CGIHTTPRequestHandler",
192 "CGIHTTPRequestHandler",
189 ))
193 ))
190
194
191 else:
195 else:
192 import urllib.request
196 import urllib.request
193 urlreq._registeraliases(urllib.request, (
197 urlreq._registeraliases(urllib.request, (
194 "AbstractHTTPHandler",
198 "AbstractHTTPHandler",
195 "addclosehook",
199 "addclosehook",
196 "addinfourl",
200 "addinfourl",
197 "BaseHandler",
201 "BaseHandler",
198 "build_opener",
202 "build_opener",
199 "FileHandler",
203 "FileHandler",
200 "FTPHandler",
204 "FTPHandler",
201 "ftpwrapper",
205 "ftpwrapper",
202 "HTTPHandler",
206 "HTTPHandler",
203 "HTTPSHandler",
207 "HTTPSHandler",
204 "install_opener",
208 "install_opener",
205 "pathname2url",
209 "pathname2url",
206 "HTTPBasicAuthHandler",
210 "HTTPBasicAuthHandler",
207 "HTTPDigestAuthHandler",
211 "HTTPDigestAuthHandler",
208 "HTTPPasswordMgrWithDefaultRealm",
212 "HTTPPasswordMgrWithDefaultRealm",
209 "ProxyHandler",
213 "ProxyHandler",
210 "quote",
214 "quote",
211 "Request",
215 "Request",
212 "splitattr",
216 "splitattr",
213 "splitpasswd",
217 "splitpasswd",
214 "splitport",
218 "splitport",
215 "splituser",
219 "splituser",
216 "unquote",
220 "unquote",
217 "url2pathname",
221 "url2pathname",
218 "urlopen",
222 "urlopen",
219 ))
223 ))
220 import urllib.error
224 import urllib.error
221 urlerr._registeraliases(urllib.error, (
225 urlerr._registeraliases(urllib.error, (
222 "HTTPError",
226 "HTTPError",
223 "URLError",
227 "URLError",
224 ))
228 ))
225 import http.server
229 import http.server
226 httpserver._registeraliases(http.server, (
230 httpserver._registeraliases(http.server, (
227 "HTTPServer",
231 "HTTPServer",
228 "BaseHTTPRequestHandler",
232 "BaseHTTPRequestHandler",
229 "SimpleHTTPRequestHandler",
233 "SimpleHTTPRequestHandler",
230 "CGIHTTPRequestHandler",
234 "CGIHTTPRequestHandler",
231 ))
235 ))
General Comments 0
You need to be logged in to leave comments. Login now