Show More
@@ -21,6 +21,8 b' import traceback' | |||
|
21 | 21 | |
|
22 | 22 | from .i18n import _ |
|
23 | 23 | |
|
24 | from hgdemandimport import tracing | |
|
25 | ||
|
24 | 26 | from . import ( |
|
25 | 27 | cmdutil, |
|
26 | 28 | color, |
@@ -84,7 +86,8 b' class request(object):' | |||
|
84 | 86 | def run(): |
|
85 | 87 | "run the command in sys.argv" |
|
86 | 88 | initstdio() |
|
87 | req = request(pycompat.sysargv[1:]) | |
|
89 | with tracing.log('parse args into request'): | |
|
90 | req = request(pycompat.sysargv[1:]) | |
|
88 | 91 | err = None |
|
89 | 92 | try: |
|
90 | 93 | status = dispatch(req) |
@@ -176,182 +179,184 b' def _formatargs(args):' | |||
|
176 | 179 | |
|
177 | 180 | def dispatch(req): |
|
178 | 181 | """run the command specified in req.args; returns an integer status code""" |
|
179 | if req.ferr: | |
|
180 |
f |
|
|
181 | elif req.ui: | |
|
182 |
|
|
|
183 | else: | |
|
184 | ferr = procutil.stderr | |
|
182 | with tracing.log('dispatch.dispatch'): | |
|
183 | if req.ferr: | |
|
184 | ferr = req.ferr | |
|
185 | elif req.ui: | |
|
186 | ferr = req.ui.ferr | |
|
187 | else: | |
|
188 | ferr = procutil.stderr | |
|
185 | 189 | |
|
186 | try: | |
|
187 | if not req.ui: | |
|
188 | req.ui = uimod.ui.load() | |
|
189 | req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) | |
|
190 | if req.earlyoptions['traceback']: | |
|
191 | req.ui.setconfig('ui', 'traceback', 'on', '--traceback') | |
|
190 | try: | |
|
191 | if not req.ui: | |
|
192 | req.ui = uimod.ui.load() | |
|
193 | req.earlyoptions.update(_earlyparseopts(req.ui, req.args)) | |
|
194 | if req.earlyoptions['traceback']: | |
|
195 | req.ui.setconfig('ui', 'traceback', 'on', '--traceback') | |
|
192 | 196 | |
|
193 | # set ui streams from the request | |
|
194 | if req.fin: | |
|
195 | req.ui.fin = req.fin | |
|
196 | if req.fout: | |
|
197 | req.ui.fout = req.fout | |
|
198 | if req.ferr: | |
|
199 | req.ui.ferr = req.ferr | |
|
200 | except error.Abort as inst: | |
|
201 | ferr.write(_("abort: %s\n") % inst) | |
|
202 | if inst.hint: | |
|
203 | ferr.write(_("(%s)\n") % inst.hint) | |
|
204 | return -1 | |
|
205 | except error.ParseError as inst: | |
|
206 | _formatparse(ferr.write, inst) | |
|
207 | return -1 | |
|
197 | # set ui streams from the request | |
|
198 | if req.fin: | |
|
199 | req.ui.fin = req.fin | |
|
200 | if req.fout: | |
|
201 | req.ui.fout = req.fout | |
|
202 | if req.ferr: | |
|
203 | req.ui.ferr = req.ferr | |
|
204 | except error.Abort as inst: | |
|
205 | ferr.write(_("abort: %s\n") % inst) | |
|
206 | if inst.hint: | |
|
207 | ferr.write(_("(%s)\n") % inst.hint) | |
|
208 | return -1 | |
|
209 | except error.ParseError as inst: | |
|
210 | _formatparse(ferr.write, inst) | |
|
211 | return -1 | |
|
208 | 212 | |
|
209 | msg = _formatargs(req.args) | |
|
210 | starttime = util.timer() | |
|
211 | ret = 1 # default of Python exit code on unhandled exception | |
|
212 | try: | |
|
213 | ret = _runcatch(req) or 0 | |
|
214 | except error.ProgrammingError as inst: | |
|
215 | req.ui.error(_('** ProgrammingError: %s\n') % inst) | |
|
216 | if inst.hint: | |
|
217 | req.ui.error(_('** (%s)\n') % inst.hint) | |
|
218 | raise | |
|
219 | except KeyboardInterrupt as inst: | |
|
213 | msg = _formatargs(req.args) | |
|
214 | starttime = util.timer() | |
|
215 | ret = 1 # default of Python exit code on unhandled exception | |
|
220 | 216 | try: |
|
221 | if isinstance(inst, error.SignalInterrupt): | |
|
222 | msg = _("killed!\n") | |
|
223 | else: | |
|
224 | msg = _("interrupted!\n") | |
|
225 |
req.ui.error( |
|
|
226 | except error.SignalInterrupt: | |
|
227 | # maybe pager would quit without consuming all the output, and | |
|
228 | # SIGPIPE was raised. we cannot print anything in this case. | |
|
229 | pass | |
|
230 | except IOError as inst: | |
|
231 | if inst.errno != errno.EPIPE: | |
|
232 | raise | |
|
233 | ret = -1 | |
|
234 | finally: | |
|
235 | duration = util.timer() - starttime | |
|
236 | req.ui.flush() | |
|
237 | if req.ui.logblockedtimes: | |
|
238 | req.ui._blockedtimes['command_duration'] = duration * 1000 | |
|
239 | req.ui.log('uiblocked', 'ui blocked ms', | |
|
240 | **pycompat.strkwargs(req.ui._blockedtimes)) | |
|
241 | req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", | |
|
242 | msg, ret & 255, duration) | |
|
243 | try: | |
|
244 |
req. |
|
|
245 | except: # exiting, so no re-raises | |
|
246 | ret = ret or -1 | |
|
247 | return ret | |
|
217 | ret = _runcatch(req) or 0 | |
|
218 | except error.ProgrammingError as inst: | |
|
219 | req.ui.error(_('** ProgrammingError: %s\n') % inst) | |
|
220 | if inst.hint: | |
|
221 | req.ui.error(_('** (%s)\n') % inst.hint) | |
|
222 | raise | |
|
223 | except KeyboardInterrupt as inst: | |
|
224 | try: | |
|
225 | if isinstance(inst, error.SignalInterrupt): | |
|
226 | msg = _("killed!\n") | |
|
227 | else: | |
|
228 | msg = _("interrupted!\n") | |
|
229 | req.ui.error(msg) | |
|
230 | except error.SignalInterrupt: | |
|
231 | # maybe pager would quit without consuming all the output, and | |
|
232 | # SIGPIPE was raised. we cannot print anything in this case. | |
|
233 | pass | |
|
234 | except IOError as inst: | |
|
235 | if inst.errno != errno.EPIPE: | |
|
236 | raise | |
|
237 | ret = -1 | |
|
238 | finally: | |
|
239 | duration = util.timer() - starttime | |
|
240 | req.ui.flush() | |
|
241 | if req.ui.logblockedtimes: | |
|
242 | req.ui._blockedtimes['command_duration'] = duration * 1000 | |
|
243 | req.ui.log('uiblocked', 'ui blocked ms', | |
|
244 | **pycompat.strkwargs(req.ui._blockedtimes)) | |
|
245 | req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n", | |
|
246 | msg, ret & 255, duration) | |
|
247 | try: | |
|
248 | req._runexithandlers() | |
|
249 | except: # exiting, so no re-raises | |
|
250 | ret = ret or -1 | |
|
251 | return ret | |
|
248 | 252 | |
|
249 | 253 | def _runcatch(req): |
|
250 | def catchterm(*args): | |
|
251 | raise error.SignalInterrupt | |
|
254 | with tracing.log('dispatch._runcatch'): | |
|
255 | def catchterm(*args): | |
|
256 | raise error.SignalInterrupt | |
|
252 | 257 | |
|
253 | ui = req.ui | |
|
254 | try: | |
|
255 | for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': | |
|
256 | num = getattr(signal, name, None) | |
|
257 | if num: | |
|
258 | signal.signal(num, catchterm) | |
|
259 | except ValueError: | |
|
260 | pass # happens if called in a thread | |
|
261 | ||
|
262 | def _runcatchfunc(): | |
|
263 | realcmd = None | |
|
258 | ui = req.ui | |
|
264 | 259 | try: |
|
265 | cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {}) | |
|
266 | cmd = cmdargs[0] | |
|
267 | aliases, entry = cmdutil.findcmd(cmd, commands.table, False) | |
|
268 | realcmd = aliases[0] | |
|
269 | except (error.UnknownCommand, error.AmbiguousCommand, | |
|
270 | IndexError, getopt.GetoptError): | |
|
271 | # Don't handle this here. We know the command is | |
|
272 | # invalid, but all we're worried about for now is that | |
|
273 | # it's not a command that server operators expect to | |
|
274 | # be safe to offer to users in a sandbox. | |
|
275 | pass | |
|
276 | if realcmd == 'serve' and '--stdio' in cmdargs: | |
|
277 | # We want to constrain 'hg serve --stdio' instances pretty | |
|
278 | # closely, as many shared-ssh access tools want to grant | |
|
279 | # access to run *only* 'hg -R $repo serve --stdio'. We | |
|
280 | # restrict to exactly that set of arguments, and prohibit | |
|
281 | # any repo name that starts with '--' to prevent | |
|
282 | # shenanigans wherein a user does something like pass | |
|
283 | # --debugger or --config=ui.debugger=1 as a repo | |
|
284 | # name. This used to actually run the debugger. | |
|
285 | if (len(req.args) != 4 or | |
|
286 | req.args[0] != '-R' or | |
|
287 | req.args[1].startswith('--') or | |
|
288 | req.args[2] != 'serve' or | |
|
289 | req.args[3] != '--stdio'): | |
|
290 | raise error.Abort( | |
|
291 | _('potentially unsafe serve --stdio invocation: %s') % | |
|
292 | (stringutil.pprint(req.args),)) | |
|
260 | for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM': | |
|
261 | num = getattr(signal, name, None) | |
|
262 | if num: | |
|
263 | signal.signal(num, catchterm) | |
|
264 | except ValueError: | |
|
265 | pass # happens if called in a thread | |
|
293 | 266 | |
|
294 | try: | |
|
295 | debugger = 'pdb' | |
|
296 |
|
|
|
297 | 'pdb': pdb.set_trace | |
|
298 | } | |
|
299 | debugmortem = { | |
|
300 | 'pdb': pdb.post_mortem | |
|
301 | } | |
|
267 | def _runcatchfunc(): | |
|
268 | realcmd = None | |
|
269 | try: | |
|
270 | cmdargs = fancyopts.fancyopts( | |
|
271 | req.args[:], commands.globalopts, {}) | |
|
272 | cmd = cmdargs[0] | |
|
273 | aliases, entry = cmdutil.findcmd(cmd, commands.table, False) | |
|
274 | realcmd = aliases[0] | |
|
275 | except (error.UnknownCommand, error.AmbiguousCommand, | |
|
276 | IndexError, getopt.GetoptError): | |
|
277 | # Don't handle this here. We know the command is | |
|
278 | # invalid, but all we're worried about for now is that | |
|
279 | # it's not a command that server operators expect to | |
|
280 | # be safe to offer to users in a sandbox. | |
|
281 | pass | |
|
282 | if realcmd == 'serve' and '--stdio' in cmdargs: | |
|
283 | # We want to constrain 'hg serve --stdio' instances pretty | |
|
284 | # closely, as many shared-ssh access tools want to grant | |
|
285 | # access to run *only* 'hg -R $repo serve --stdio'. We | |
|
286 | # restrict to exactly that set of arguments, and prohibit | |
|
287 | # any repo name that starts with '--' to prevent | |
|
288 | # shenanigans wherein a user does something like pass | |
|
289 | # --debugger or --config=ui.debugger=1 as a repo | |
|
290 | # name. This used to actually run the debugger. | |
|
291 | if (len(req.args) != 4 or | |
|
292 | req.args[0] != '-R' or | |
|
293 | req.args[1].startswith('--') or | |
|
294 | req.args[2] != 'serve' or | |
|
295 | req.args[3] != '--stdio'): | |
|
296 | raise error.Abort( | |
|
297 | _('potentially unsafe serve --stdio invocation: %s') % | |
|
298 | (stringutil.pprint(req.args),)) | |
|
302 | 299 | |
|
303 | # read --config before doing anything else | |
|
304 | # (e.g. to change trust settings for reading .hg/hgrc) | |
|
305 | cfgs = _parseconfig(req.ui, req.earlyoptions['config']) | |
|
306 | ||
|
307 |
|
|
|
308 | # copy configs that were passed on the cmdline (--config) to | |
|
309 | # the repo ui | |
|
310 | for sec, name, val in cfgs: | |
|
311 | req.repo.ui.setconfig(sec, name, val, source='--config') | |
|
300 | try: | |
|
301 | debugger = 'pdb' | |
|
302 | debugtrace = { | |
|
303 | 'pdb': pdb.set_trace | |
|
304 | } | |
|
305 | debugmortem = { | |
|
306 | 'pdb': pdb.post_mortem | |
|
307 | } | |
|
312 | 308 | |
|
313 | # developer config: ui.debugger | |
|
314 | debugger = ui.config("ui", "debugger") | |
|
315 | debugmod = pdb | |
|
316 | if not debugger or ui.plain(): | |
|
317 | # if we are in HGPLAIN mode, then disable custom debugging | |
|
318 | debugger = 'pdb' | |
|
319 | elif req.earlyoptions['debugger']: | |
|
320 | # This import can be slow for fancy debuggers, so only | |
|
321 | # do it when absolutely necessary, i.e. when actual | |
|
322 | # debugging has been requested | |
|
323 | with demandimport.deactivated(): | |
|
324 | try: | |
|
325 | debugmod = __import__(debugger) | |
|
326 | except ImportError: | |
|
327 | pass # Leave debugmod = pdb | |
|
309 | # read --config before doing anything else | |
|
310 | # (e.g. to change trust settings for reading .hg/hgrc) | |
|
311 | cfgs = _parseconfig(req.ui, req.earlyoptions['config']) | |
|
312 | ||
|
313 | if req.repo: | |
|
314 | # copy configs that were passed on the cmdline (--config) to | |
|
315 | # the repo ui | |
|
316 | for sec, name, val in cfgs: | |
|
317 | req.repo.ui.setconfig(sec, name, val, source='--config') | |
|
328 | 318 | |
|
329 | debugtrace[debugger] = debugmod.set_trace | |
|
330 | debugmortem[debugger] = debugmod.post_mortem | |
|
319 | # developer config: ui.debugger | |
|
320 | debugger = ui.config("ui", "debugger") | |
|
321 | debugmod = pdb | |
|
322 | if not debugger or ui.plain(): | |
|
323 | # if we are in HGPLAIN mode, then disable custom debugging | |
|
324 | debugger = 'pdb' | |
|
325 | elif req.earlyoptions['debugger']: | |
|
326 | # This import can be slow for fancy debuggers, so only | |
|
327 | # do it when absolutely necessary, i.e. when actual | |
|
328 | # debugging has been requested | |
|
329 | with demandimport.deactivated(): | |
|
330 | try: | |
|
331 | debugmod = __import__(debugger) | |
|
332 | except ImportError: | |
|
333 | pass # Leave debugmod = pdb | |
|
331 | 334 | |
|
332 | # enter the debugger before command execution | |
|
333 | if req.earlyoptions['debugger']: | |
|
334 | ui.warn(_("entering debugger - " | |
|
335 | "type c to continue starting hg or h for help\n")) | |
|
335 | debugtrace[debugger] = debugmod.set_trace | |
|
336 | debugmortem[debugger] = debugmod.post_mortem | |
|
336 | 337 | |
|
337 | if (debugger != 'pdb' and | |
|
338 | debugtrace[debugger] == debugtrace['pdb']): | |
|
339 |
ui.warn(_(" |
|
|
340 | "but its module was not found\n") % debugger) | |
|
341 | with demandimport.deactivated(): | |
|
342 | debugtrace[debugger]() | |
|
343 | try: | |
|
344 | return _dispatch(req) | |
|
345 | finally: | |
|
346 | ui.flush() | |
|
347 | except: # re-raises | |
|
348 | # enter the debugger when we hit an exception | |
|
349 | if req.earlyoptions['debugger']: | |
|
350 | traceback.print_exc() | |
|
351 | debugmortem[debugger](sys.exc_info()[2]) | |
|
352 | raise | |
|
338 | # enter the debugger before command execution | |
|
339 | if req.earlyoptions['debugger']: | |
|
340 | ui.warn(_("entering debugger - " | |
|
341 | "type c to continue starting hg or h for help\n")) | |
|
353 | 342 | |
|
354 | return _callcatch(ui, _runcatchfunc) | |
|
343 | if (debugger != 'pdb' and | |
|
344 | debugtrace[debugger] == debugtrace['pdb']): | |
|
345 | ui.warn(_("%s debugger specified " | |
|
346 | "but its module was not found\n") % debugger) | |
|
347 | with demandimport.deactivated(): | |
|
348 | debugtrace[debugger]() | |
|
349 | try: | |
|
350 | return _dispatch(req) | |
|
351 | finally: | |
|
352 | ui.flush() | |
|
353 | except: # re-raises | |
|
354 | # enter the debugger when we hit an exception | |
|
355 | if req.earlyoptions['debugger']: | |
|
356 | traceback.print_exc() | |
|
357 | debugmortem[debugger](sys.exc_info()[2]) | |
|
358 | raise | |
|
359 | return _callcatch(ui, _runcatchfunc) | |
|
355 | 360 | |
|
356 | 361 | def _callcatch(ui, func): |
|
357 | 362 | """like scmutil.callcatch but handles more high-level exceptions about |
General Comments 0
You need to be logged in to leave comments.
Login now