comparison mercurial/commands.py @ 4545:aea8fd7fb5e2

dispatch: move signal and exception handling to its own function
author Matt Mackall <mpm@selenic.com>
date Mon, 11 Jun 2007 21:09:23 -0500
parents 930ed513c864
children 5c8130691692
comparison
equal deleted inserted replaced
4544:930ed513c864 4545:aea8fd7fb5e2
3032 norepo = ("clone init version help debugancestor debugcomplete debugdata" 3032 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3033 " debugindex debugindexdot debugdate debuginstall") 3033 " debugindex debugindexdot debugdate debuginstall")
3034 optionalrepo = ("paths serve showconfig") 3034 optionalrepo = ("paths serve showconfig")
3035 3035
3036 def run(): 3036 def run():
3037 sys.exit(dispatch(sys.argv[1:]))
3038
3039 def findpossible(ui, cmd):
3040 """
3041 Return cmd -> (aliases, command table entry)
3042 for each matching command.
3043 Return debug commands (or their aliases) only if no normal command matches.
3044 """
3045 choice = {}
3046 debugchoice = {}
3047 for e in table.keys():
3048 aliases = e.lstrip("^").split("|")
3049 found = None
3050 if cmd in aliases:
3051 found = cmd
3052 elif not ui.config("ui", "strict"):
3053 for a in aliases:
3054 if a.startswith(cmd):
3055 found = a
3056 break
3057 if found is not None:
3058 if aliases[0].startswith("debug") or found.startswith("debug"):
3059 debugchoice[found] = (aliases, table[e])
3060 else:
3061 choice[found] = (aliases, table[e])
3062
3063 if not choice and debugchoice:
3064 choice = debugchoice
3065
3066 return choice
3067
3068 def findcmd(ui, cmd):
3069 """Return (aliases, command table entry) for command string."""
3070 choice = findpossible(ui, cmd)
3071
3072 if choice.has_key(cmd):
3073 return choice[cmd]
3074
3075 if len(choice) > 1:
3076 clist = choice.keys()
3077 clist.sort()
3078 raise AmbiguousCommand(cmd, clist)
3079
3080 if choice:
3081 return choice.values()[0]
3082
3083 raise UnknownCommand(cmd)
3084
3085 class ParseError(Exception):
3086 """Exception raised on errors in parsing the command line."""
3087
3088 def parse(ui, args):
3089 options = {}
3090 cmdoptions = {}
3091
3092 try:
3093 args = fancyopts.fancyopts(args, globalopts, options)
3094 except fancyopts.getopt.GetoptError, inst:
3095 raise ParseError(None, inst)
3096
3097 if args:
3098 cmd, args = args[0], args[1:]
3099 aliases, i = findcmd(ui, cmd)
3100 cmd = aliases[0]
3101 defaults = ui.config("defaults", cmd)
3102 if defaults:
3103 args = shlex.split(defaults) + args
3104 c = list(i[1])
3105 else:
3106 cmd = None
3107 c = []
3108
3109 # combine global options into local
3110 for o in globalopts:
3111 c.append((o[0], o[1], options[o[1]], o[3]))
3112
3113 try:
3114 args = fancyopts.fancyopts(args, c, cmdoptions)
3115 except fancyopts.getopt.GetoptError, inst:
3116 raise ParseError(cmd, inst)
3117
3118 # separate global options back out
3119 for o in globalopts:
3120 n = o[1]
3121 options[n] = cmdoptions[n]
3122 del cmdoptions[n]
3123
3124 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3125
3126 def parseconfig(config):
3127 """parse the --config options from the command line"""
3128 parsed = []
3129 for cfg in config:
3130 try:
3131 name, value = cfg.split('=', 1)
3132 section, name = name.split('.', 1)
3133 if not section or not name:
3134 raise IndexError
3135 parsed.append((section, name, value))
3136 except (IndexError, ValueError):
3137 raise util.Abort(_('malformed --config option: %s') % cfg)
3138 return parsed
3139
3140 def catchterm(*args):
3141 raise util.SignalInterrupt
3142
3143 def dispatch(args):
3144 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3145 num = getattr(signal, name, None)
3146 if num: signal.signal(num, catchterm)
3147
3148 try: 3037 try:
3149 u = ui.ui(traceback='--traceback' in sys.argv[1:]) 3038 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3150 except util.Abort, inst: 3039 except util.Abort, inst:
3151 sys.stderr.write(_("abort: %s\n") % inst) 3040 sys.stderr.write(_("abort: %s\n") % inst)
3152 return -1 3041 return -1
3153 3042 sys.exit(runcatch(u, sys.argv[1:]))
3154 extensions.loadall(u) 3043
3155 u.addreadhook(extensions.loadall) 3044 def runcatch(u, args):
3045 def catchterm(*args):
3046 raise util.SignalInterrupt
3047
3048 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3049 num = getattr(signal, name, None)
3050 if num: signal.signal(num, catchterm)
3156 3051
3157 try: 3052 try:
3158 cmd, func, args, options, cmdoptions = parse(u, args) 3053 return dispatch(u, args)
3159 if options["encoding"]:
3160 util._encoding = options["encoding"]
3161 if options["encodingmode"]:
3162 util._encodingmode = options["encodingmode"]
3163 if options["time"]:
3164 def get_times():
3165 t = os.times()
3166 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3167 t = (t[0], t[1], t[2], t[3], time.clock())
3168 return t
3169 s = get_times()
3170 def print_time():
3171 t = get_times()
3172 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3173 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3174 atexit.register(print_time)
3175
3176 # enter the debugger before command execution
3177 if options['debugger']:
3178 pdb.set_trace()
3179
3180 try:
3181 if options['cwd']:
3182 os.chdir(options['cwd'])
3183
3184 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3185 not options["noninteractive"], options["traceback"],
3186 parseconfig(options["config"]))
3187
3188 path = u.expandpath(options["repository"]) or ""
3189 repo = path and hg.repository(u, path=path) or None
3190 if repo and not repo.local():
3191 raise util.Abort(_("repository '%s' is not local") % path)
3192
3193 if options['help']:
3194 return help_(u, cmd, options['version'])
3195 elif options['version']:
3196 return version_(u)
3197 elif not cmd:
3198 return help_(u, 'shortlist')
3199
3200 if cmd not in norepo.split():
3201 try:
3202 if not repo:
3203 repo = hg.repository(u, path=path)
3204 u = repo.ui
3205 except hg.RepoError:
3206 if cmd not in optionalrepo.split():
3207 raise
3208 d = lambda: func(u, repo, *args, **cmdoptions)
3209 else:
3210 d = lambda: func(u, *args, **cmdoptions)
3211
3212 try:
3213 if options['profile']:
3214 import hotshot, hotshot.stats
3215 prof = hotshot.Profile("hg.prof")
3216 try:
3217 try:
3218 return prof.runcall(d)
3219 except:
3220 try:
3221 u.warn(_('exception raised - generating '
3222 'profile anyway\n'))
3223 except:
3224 pass
3225 raise
3226 finally:
3227 prof.close()
3228 stats = hotshot.stats.load("hg.prof")
3229 stats.strip_dirs()
3230 stats.sort_stats('time', 'calls')
3231 stats.print_stats(40)
3232 elif options['lsprof']:
3233 try:
3234 from mercurial import lsprof
3235 except ImportError:
3236 raise util.Abort(_(
3237 'lsprof not available - install from '
3238 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3239 p = lsprof.Profiler()
3240 p.enable(subcalls=True)
3241 try:
3242 return d()
3243 finally:
3244 p.disable()
3245 stats = lsprof.Stats(p.getstats())
3246 stats.sort()
3247 stats.pprint(top=10, file=sys.stderr, climit=5)
3248 else:
3249 return d()
3250 finally:
3251 u.flush()
3252 except:
3253 # enter the debugger when we hit an exception
3254 if options['debugger']:
3255 pdb.post_mortem(sys.exc_info()[2])
3256 u.print_exc()
3257 raise
3258 except ParseError, inst:
3259 if inst.args[0]:
3260 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3261 help_(u, inst.args[0])
3262 else:
3263 u.warn(_("hg: %s\n") % inst.args[1])
3264 help_(u, 'shortlist')
3265 except AmbiguousCommand, inst:
3266 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3267 (inst.args[0], " ".join(inst.args[1])))
3268 except UnknownCommand, inst:
3269 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3270 help_(u, 'shortlist')
3271 except hg.RepoError, inst: 3054 except hg.RepoError, inst:
3272 u.warn(_("abort: %s!\n") % inst) 3055 u.warn(_("abort: %s!\n") % inst)
3273 except lock.LockHeld, inst: 3056 except lock.LockHeld, inst:
3274 if inst.errno == errno.ETIMEDOUT: 3057 if inst.errno == errno.ETIMEDOUT:
3275 reason = _('timed out waiting for lock held by %s') % inst.locker 3058 reason = _('timed out waiting for lock held by %s') % inst.locker
3348 u.warn(_("** Mercurial Distributed SCM (version %s)\n") 3131 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3349 % version.get_version()) 3132 % version.get_version())
3350 raise 3133 raise
3351 3134
3352 return -1 3135 return -1
3136
3137 def findpossible(ui, cmd):
3138 """
3139 Return cmd -> (aliases, command table entry)
3140 for each matching command.
3141 Return debug commands (or their aliases) only if no normal command matches.
3142 """
3143 choice = {}
3144 debugchoice = {}
3145 for e in table.keys():
3146 aliases = e.lstrip("^").split("|")
3147 found = None
3148 if cmd in aliases:
3149 found = cmd
3150 elif not ui.config("ui", "strict"):
3151 for a in aliases:
3152 if a.startswith(cmd):
3153 found = a
3154 break
3155 if found is not None:
3156 if aliases[0].startswith("debug") or found.startswith("debug"):
3157 debugchoice[found] = (aliases, table[e])
3158 else:
3159 choice[found] = (aliases, table[e])
3160
3161 if not choice and debugchoice:
3162 choice = debugchoice
3163
3164 return choice
3165
3166 def findcmd(ui, cmd):
3167 """Return (aliases, command table entry) for command string."""
3168 choice = findpossible(ui, cmd)
3169
3170 if choice.has_key(cmd):
3171 return choice[cmd]
3172
3173 if len(choice) > 1:
3174 clist = choice.keys()
3175 clist.sort()
3176 raise AmbiguousCommand(cmd, clist)
3177
3178 if choice:
3179 return choice.values()[0]
3180
3181 raise UnknownCommand(cmd)
3182
3183 class ParseError(Exception):
3184 """Exception raised on errors in parsing the command line."""
3185
3186 def parse(ui, args):
3187 options = {}
3188 cmdoptions = {}
3189
3190 try:
3191 args = fancyopts.fancyopts(args, globalopts, options)
3192 except fancyopts.getopt.GetoptError, inst:
3193 raise ParseError(None, inst)
3194
3195 if args:
3196 cmd, args = args[0], args[1:]
3197 aliases, i = findcmd(ui, cmd)
3198 cmd = aliases[0]
3199 defaults = ui.config("defaults", cmd)
3200 if defaults:
3201 args = shlex.split(defaults) + args
3202 c = list(i[1])
3203 else:
3204 cmd = None
3205 c = []
3206
3207 # combine global options into local
3208 for o in globalopts:
3209 c.append((o[0], o[1], options[o[1]], o[3]))
3210
3211 try:
3212 args = fancyopts.fancyopts(args, c, cmdoptions)
3213 except fancyopts.getopt.GetoptError, inst:
3214 raise ParseError(cmd, inst)
3215
3216 # separate global options back out
3217 for o in globalopts:
3218 n = o[1]
3219 options[n] = cmdoptions[n]
3220 del cmdoptions[n]
3221
3222 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3223
3224 def parseconfig(config):
3225 """parse the --config options from the command line"""
3226 parsed = []
3227 for cfg in config:
3228 try:
3229 name, value = cfg.split('=', 1)
3230 section, name = name.split('.', 1)
3231 if not section or not name:
3232 raise IndexError
3233 parsed.append((section, name, value))
3234 except (IndexError, ValueError):
3235 raise util.Abort(_('malformed --config option: %s') % cfg)
3236 return parsed
3237
3238 def dispatch(u, args):
3239 extensions.loadall(u)
3240 u.addreadhook(extensions.loadall)
3241
3242 try:
3243 cmd, func, args, options, cmdoptions = parse(u, args)
3244 if options["encoding"]:
3245 util._encoding = options["encoding"]
3246 if options["encodingmode"]:
3247 util._encodingmode = options["encodingmode"]
3248 if options["time"]:
3249 def get_times():
3250 t = os.times()
3251 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3252 t = (t[0], t[1], t[2], t[3], time.clock())
3253 return t
3254 s = get_times()
3255 def print_time():
3256 t = get_times()
3257 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3258 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3259 atexit.register(print_time)
3260
3261 # enter the debugger before command execution
3262 if options['debugger']:
3263 pdb.set_trace()
3264
3265 try:
3266 if options['cwd']:
3267 os.chdir(options['cwd'])
3268
3269 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3270 not options["noninteractive"], options["traceback"],
3271 parseconfig(options["config"]))
3272
3273 path = u.expandpath(options["repository"]) or ""
3274 repo = path and hg.repository(u, path=path) or None
3275 if repo and not repo.local():
3276 raise util.Abort(_("repository '%s' is not local") % path)
3277
3278 if options['help']:
3279 return help_(u, cmd, options['version'])
3280 elif options['version']:
3281 return version_(u)
3282 elif not cmd:
3283 return help_(u, 'shortlist')
3284
3285 if cmd not in norepo.split():
3286 try:
3287 if not repo:
3288 repo = hg.repository(u, path=path)
3289 u = repo.ui
3290 except hg.RepoError:
3291 if cmd not in optionalrepo.split():
3292 raise
3293 d = lambda: func(u, repo, *args, **cmdoptions)
3294 else:
3295 d = lambda: func(u, *args, **cmdoptions)
3296
3297 try:
3298 if options['profile']:
3299 import hotshot, hotshot.stats
3300 prof = hotshot.Profile("hg.prof")
3301 try:
3302 try:
3303 return prof.runcall(d)
3304 except:
3305 try:
3306 u.warn(_('exception raised - generating '
3307 'profile anyway\n'))
3308 except:
3309 pass
3310 raise
3311 finally:
3312 prof.close()
3313 stats = hotshot.stats.load("hg.prof")
3314 stats.strip_dirs()
3315 stats.sort_stats('time', 'calls')
3316 stats.print_stats(40)
3317 elif options['lsprof']:
3318 try:
3319 from mercurial import lsprof
3320 except ImportError:
3321 raise util.Abort(_(
3322 'lsprof not available - install from '
3323 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3324 p = lsprof.Profiler()
3325 p.enable(subcalls=True)
3326 try:
3327 return d()
3328 finally:
3329 p.disable()
3330 stats = lsprof.Stats(p.getstats())
3331 stats.sort()
3332 stats.pprint(top=10, file=sys.stderr, climit=5)
3333 else:
3334 return d()
3335 finally:
3336 u.flush()
3337 except:
3338 # enter the debugger when we hit an exception
3339 if options['debugger']:
3340 pdb.post_mortem(sys.exc_info()[2])
3341 u.print_exc()
3342 raise
3343 except ParseError, inst:
3344 if inst.args[0]:
3345 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3346 help_(u, inst.args[0])
3347 else:
3348 u.warn(_("hg: %s\n") % inst.args[1])
3349 help_(u, 'shortlist')
3350 except AmbiguousCommand, inst:
3351 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3352 (inst.args[0], " ".join(inst.args[1])))
3353 except UnknownCommand, inst:
3354 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3355 help_(u, 'shortlist')