##// END OF EJS Templates
extensions: load and configure extensions in well-defined phases...
Martin Geisler -
r9410:1c83938b default
parent child Browse files
Show More
@@ -1,501 +1,504
1 1 # dispatch.py - command dispatching for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as _ui
13 13
14 14 def run():
15 15 "run the command in sys.argv"
16 16 sys.exit(dispatch(sys.argv[1:]))
17 17
18 18 def dispatch(args):
19 19 "run the command specified in args"
20 20 try:
21 21 u = _ui.ui()
22 22 if '--traceback' in args:
23 23 u.setconfig('ui', 'traceback', 'on')
24 24 except util.Abort, inst:
25 25 sys.stderr.write(_("abort: %s\n") % inst)
26 26 return -1
27 27 return _runcatch(u, args)
28 28
29 29 def _runcatch(ui, args):
30 30 def catchterm(*args):
31 31 raise error.SignalInterrupt
32 32
33 33 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
34 34 num = getattr(signal, name, None)
35 35 if num: signal.signal(num, catchterm)
36 36
37 37 try:
38 38 try:
39 39 # enter the debugger before command execution
40 40 if '--debugger' in args:
41 41 pdb.set_trace()
42 42 try:
43 43 return _dispatch(ui, args)
44 44 finally:
45 45 ui.flush()
46 46 except:
47 47 # enter the debugger when we hit an exception
48 48 if '--debugger' in args:
49 49 pdb.post_mortem(sys.exc_info()[2])
50 50 ui.traceback()
51 51 raise
52 52
53 53 # Global exception handling, alphabetically
54 54 # Mercurial-specific first, followed by built-in and library exceptions
55 55 except error.AmbiguousCommand, inst:
56 56 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
57 57 (inst.args[0], " ".join(inst.args[1])))
58 58 except error.ConfigError, inst:
59 59 ui.warn(_("hg: %s\n") % inst.args[0])
60 60 except error.LockHeld, inst:
61 61 if inst.errno == errno.ETIMEDOUT:
62 62 reason = _('timed out waiting for lock held by %s') % inst.locker
63 63 else:
64 64 reason = _('lock held by %s') % inst.locker
65 65 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
66 66 except error.LockUnavailable, inst:
67 67 ui.warn(_("abort: could not lock %s: %s\n") %
68 68 (inst.desc or inst.filename, inst.strerror))
69 69 except error.ParseError, inst:
70 70 if inst.args[0]:
71 71 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
72 72 commands.help_(ui, inst.args[0])
73 73 else:
74 74 ui.warn(_("hg: %s\n") % inst.args[1])
75 75 commands.help_(ui, 'shortlist')
76 76 except error.RepoError, inst:
77 77 ui.warn(_("abort: %s!\n") % inst)
78 78 except error.ResponseError, inst:
79 79 ui.warn(_("abort: %s") % inst.args[0])
80 80 if not isinstance(inst.args[1], basestring):
81 81 ui.warn(" %r\n" % (inst.args[1],))
82 82 elif not inst.args[1]:
83 83 ui.warn(_(" empty string\n"))
84 84 else:
85 85 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
86 86 except error.RevlogError, inst:
87 87 ui.warn(_("abort: %s!\n") % inst)
88 88 except error.SignalInterrupt:
89 89 ui.warn(_("killed!\n"))
90 90 except error.UnknownCommand, inst:
91 91 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
92 92 commands.help_(ui, 'shortlist')
93 93 except util.Abort, inst:
94 94 ui.warn(_("abort: %s\n") % inst)
95 95 except ImportError, inst:
96 96 m = str(inst).split()[-1]
97 97 ui.warn(_("abort: could not import module %s!\n") % m)
98 98 if m in "mpatch bdiff".split():
99 99 ui.warn(_("(did you forget to compile extensions?)\n"))
100 100 elif m in "zlib".split():
101 101 ui.warn(_("(is your Python install correct?)\n"))
102 102 except IOError, inst:
103 103 if hasattr(inst, "code"):
104 104 ui.warn(_("abort: %s\n") % inst)
105 105 elif hasattr(inst, "reason"):
106 106 try: # usually it is in the form (errno, strerror)
107 107 reason = inst.reason.args[1]
108 108 except: # it might be anything, for example a string
109 109 reason = inst.reason
110 110 ui.warn(_("abort: error: %s\n") % reason)
111 111 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
112 112 if ui.debugflag:
113 113 ui.warn(_("broken pipe\n"))
114 114 elif getattr(inst, "strerror", None):
115 115 if getattr(inst, "filename", None):
116 116 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
117 117 else:
118 118 ui.warn(_("abort: %s\n") % inst.strerror)
119 119 else:
120 120 raise
121 121 except OSError, inst:
122 122 if getattr(inst, "filename", None):
123 123 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
124 124 else:
125 125 ui.warn(_("abort: %s\n") % inst.strerror)
126 126 except KeyboardInterrupt:
127 127 try:
128 128 ui.warn(_("interrupted!\n"))
129 129 except IOError, inst:
130 130 if inst.errno == errno.EPIPE:
131 131 if ui.debugflag:
132 132 ui.warn(_("\nbroken pipe\n"))
133 133 else:
134 134 raise
135 135 except MemoryError:
136 136 ui.warn(_("abort: out of memory\n"))
137 137 except SystemExit, inst:
138 138 # Commands shouldn't sys.exit directly, but give a return code.
139 139 # Just in case catch this and and pass exit code to caller.
140 140 return inst.code
141 141 except socket.error, inst:
142 142 ui.warn(_("abort: %s\n") % inst.args[-1])
143 143 except:
144 144 ui.warn(_("** unknown exception encountered, details follow\n"))
145 145 ui.warn(_("** report bug details to "
146 146 "http://mercurial.selenic.com/bts/\n"))
147 147 ui.warn(_("** or mercurial@selenic.com\n"))
148 148 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
149 149 % util.version())
150 150 ui.warn(_("** Extensions loaded: %s\n")
151 151 % ", ".join([x[0] for x in extensions.extensions()]))
152 152 raise
153 153
154 154 return -1
155 155
156 156 def _findrepo(p):
157 157 while not os.path.isdir(os.path.join(p, ".hg")):
158 158 oldp, p = p, os.path.dirname(p)
159 159 if p == oldp:
160 160 return None
161 161
162 162 return p
163 163
164 164 def aliasargs(fn):
165 165 if hasattr(fn, 'args'):
166 166 return fn.args
167 167 return []
168 168
169 169 class cmdalias(object):
170 170 def __init__(self, name, definition, cmdtable):
171 171 self.name = name
172 172 self.definition = definition
173 173 self.args = []
174 174 self.opts = []
175 175 self.help = ''
176 176 self.norepo = True
177 177
178 178 try:
179 179 cmdutil.findcmd(self.name, cmdtable, True)
180 180 self.shadows = True
181 181 except error.UnknownCommand:
182 182 self.shadows = False
183 183
184 184 if not self.definition:
185 185 def fn(ui, *args):
186 186 ui.warn(_("no definition for alias '%s'\n") % self.name)
187 187 return 1
188 188 self.fn = fn
189 189
190 190 return
191 191
192 192 args = shlex.split(self.definition)
193 193 cmd = args.pop(0)
194 194 opts = []
195 195 help = ''
196 196
197 197 try:
198 198 self.fn, self.opts, self.help = cmdutil.findcmd(cmd, cmdtable, False)[1]
199 199 self.args = aliasargs(self.fn) + args
200 200 if cmd not in commands.norepo.split(' '):
201 201 self.norepo = False
202 202 except error.UnknownCommand:
203 203 def fn(ui, *args):
204 204 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
205 205 % (self.name, cmd))
206 206 return 1
207 207 self.fn = fn
208 208 except error.AmbiguousCommand:
209 209 def fn(ui, *args):
210 210 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
211 211 % (self.name, cmd))
212 212 return 1
213 213 self.fn = fn
214 214
215 215 def __call__(self, ui, *args, **opts):
216 216 if self.shadows:
217 217 ui.debug(_("alias '%s' shadows command\n") % self.name)
218 218
219 219 return self.fn(ui, *args, **opts)
220 220
221 221 def addaliases(ui, cmdtable):
222 222 # aliases are processed after extensions have been loaded, so they
223 223 # may use extension commands. Aliases can also use other alias definitions,
224 224 # but only if they have been defined prior to the current definition.
225 225 for alias, definition in ui.configitems('alias'):
226 226 aliasdef = cmdalias(alias, definition, cmdtable)
227 227 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
228 228 if aliasdef.norepo:
229 229 commands.norepo += ' %s' % alias
230 230
231 231 def _parse(ui, args):
232 232 options = {}
233 233 cmdoptions = {}
234 234
235 235 try:
236 236 args = fancyopts.fancyopts(args, commands.globalopts, options)
237 237 except fancyopts.getopt.GetoptError, inst:
238 238 raise error.ParseError(None, inst)
239 239
240 240 if args:
241 241 cmd, args = args[0], args[1:]
242 242 aliases, i = cmdutil.findcmd(cmd, commands.table,
243 243 ui.config("ui", "strict"))
244 244 cmd = aliases[0]
245 245 args = aliasargs(i[0]) + args
246 246 defaults = ui.config("defaults", cmd)
247 247 if defaults:
248 248 args = shlex.split(defaults) + args
249 249 c = list(i[1])
250 250 else:
251 251 cmd = None
252 252 c = []
253 253
254 254 # combine global options into local
255 255 for o in commands.globalopts:
256 256 c.append((o[0], o[1], options[o[1]], o[3]))
257 257
258 258 try:
259 259 args = fancyopts.fancyopts(args, c, cmdoptions, True)
260 260 except fancyopts.getopt.GetoptError, inst:
261 261 raise error.ParseError(cmd, inst)
262 262
263 263 # separate global options back out
264 264 for o in commands.globalopts:
265 265 n = o[1]
266 266 options[n] = cmdoptions[n]
267 267 del cmdoptions[n]
268 268
269 269 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
270 270
271 271 def _parseconfig(ui, config):
272 272 """parse the --config options from the command line"""
273 273 for cfg in config:
274 274 try:
275 275 name, value = cfg.split('=', 1)
276 276 section, name = name.split('.', 1)
277 277 if not section or not name:
278 278 raise IndexError
279 279 ui.setconfig(section, name, value)
280 280 except (IndexError, ValueError):
281 281 raise util.Abort(_('malformed --config option: %s') % cfg)
282 282
283 283 def _earlygetopt(aliases, args):
284 284 """Return list of values for an option (or aliases).
285 285
286 286 The values are listed in the order they appear in args.
287 287 The options and values are removed from args.
288 288 """
289 289 try:
290 290 argcount = args.index("--")
291 291 except ValueError:
292 292 argcount = len(args)
293 293 shortopts = [opt for opt in aliases if len(opt) == 2]
294 294 values = []
295 295 pos = 0
296 296 while pos < argcount:
297 297 if args[pos] in aliases:
298 298 if pos + 1 >= argcount:
299 299 # ignore and let getopt report an error if there is no value
300 300 break
301 301 del args[pos]
302 302 values.append(args.pop(pos))
303 303 argcount -= 2
304 304 elif args[pos][:2] in shortopts:
305 305 # short option can have no following space, e.g. hg log -Rfoo
306 306 values.append(args.pop(pos)[2:])
307 307 argcount -= 1
308 308 else:
309 309 pos += 1
310 310 return values
311 311
312 312 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
313 313 # run pre-hook, and abort if it fails
314 314 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
315 315 if ret:
316 316 return ret
317 317 ret = _runcommand(ui, options, cmd, d)
318 318 # run post-hook, passing command result
319 319 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
320 320 result = ret)
321 321 return ret
322 322
323 323 _loaded = set()
324 324 def _dispatch(ui, args):
325 325 # read --config before doing anything else
326 326 # (e.g. to change trust settings for reading .hg/hgrc)
327 327 _parseconfig(ui, _earlygetopt(['--config'], args))
328 328
329 329 # check for cwd
330 330 cwd = _earlygetopt(['--cwd'], args)
331 331 if cwd:
332 332 os.chdir(cwd[-1])
333 333
334 334 # read the local repository .hgrc into a local ui object
335 335 path = _findrepo(os.getcwd()) or ""
336 336 if not path:
337 337 lui = ui
338 338 if path:
339 339 try:
340 340 lui = ui.copy()
341 341 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
342 342 except IOError:
343 343 pass
344 344
345 345 # now we can expand paths, even ones in .hg/hgrc
346 346 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
347 347 if rpath:
348 348 path = lui.expandpath(rpath[-1])
349 349 lui = ui.copy()
350 350 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
351 351
352 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
353 # reposetup. Programs like TortoiseHg will call _dispatch several
354 # times so we keep track of configured extensions in _loaded.
352 355 extensions.loadall(lui)
353 for name, module in extensions.extensions():
354 if name in _loaded:
355 continue
356 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
356 357
357 # setup extensions
358 # TODO this should be generalized to scheme, where extensions can
359 # redepend on other extensions. then we should toposort them, and
360 # do initialization in correct order
358 # (uisetup is handled in extensions.loadall)
359
360 for name, module in exts:
361 361 extsetup = getattr(module, 'extsetup', None)
362 362 if extsetup:
363 363 extsetup()
364 364
365 for name, module in exts:
365 366 cmdtable = getattr(module, 'cmdtable', {})
366 367 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
367 368 if overrides:
368 369 ui.warn(_("extension '%s' overrides commands: %s\n")
369 370 % (name, " ".join(overrides)))
370 371 commands.table.update(cmdtable)
371 372 _loaded.add(name)
372 373
374 # (reposetup is handled in hg.repository)
375
373 376 addaliases(lui, commands.table)
374 377
375 378 # check for fallback encoding
376 379 fallback = lui.config('ui', 'fallbackencoding')
377 380 if fallback:
378 381 encoding.fallbackencoding = fallback
379 382
380 383 fullargs = args
381 384 cmd, func, args, options, cmdoptions = _parse(lui, args)
382 385
383 386 if options["config"]:
384 387 raise util.Abort(_("Option --config may not be abbreviated!"))
385 388 if options["cwd"]:
386 389 raise util.Abort(_("Option --cwd may not be abbreviated!"))
387 390 if options["repository"]:
388 391 raise util.Abort(_(
389 392 "Option -R has to be separated from other options (e.g. not -qR) "
390 393 "and --repository may only be abbreviated as --repo!"))
391 394
392 395 if options["encoding"]:
393 396 encoding.encoding = options["encoding"]
394 397 if options["encodingmode"]:
395 398 encoding.encodingmode = options["encodingmode"]
396 399 if options["time"]:
397 400 def get_times():
398 401 t = os.times()
399 402 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
400 403 t = (t[0], t[1], t[2], t[3], time.clock())
401 404 return t
402 405 s = get_times()
403 406 def print_time():
404 407 t = get_times()
405 408 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
406 409 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
407 410 atexit.register(print_time)
408 411
409 412 if options['verbose'] or options['debug'] or options['quiet']:
410 413 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
411 414 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
412 415 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
413 416 if options['traceback']:
414 417 ui.setconfig('ui', 'traceback', 'on')
415 418 if options['noninteractive']:
416 419 ui.setconfig('ui', 'interactive', 'off')
417 420
418 421 if options['help']:
419 422 return commands.help_(ui, cmd, options['version'])
420 423 elif options['version']:
421 424 return commands.version_(ui)
422 425 elif not cmd:
423 426 return commands.help_(ui, 'shortlist')
424 427
425 428 repo = None
426 429 if cmd not in commands.norepo.split():
427 430 try:
428 431 repo = hg.repository(ui, path=path)
429 432 ui = repo.ui
430 433 if not repo.local():
431 434 raise util.Abort(_("repository '%s' is not local") % path)
432 435 ui.setconfig("bundle", "mainreporoot", repo.root)
433 436 except error.RepoError:
434 437 if cmd not in commands.optionalrepo.split():
435 438 if args and not path: # try to infer -R from command args
436 439 repos = map(_findrepo, args)
437 440 guess = repos[0]
438 441 if guess and repos.count(guess) == len(repos):
439 442 return _dispatch(ui, ['--repository', guess] + fullargs)
440 443 if not path:
441 444 raise error.RepoError(_("There is no Mercurial repository"
442 445 " here (.hg not found)"))
443 446 raise
444 447 args.insert(0, repo)
445 448 elif rpath:
446 449 ui.warn("warning: --repository ignored\n")
447 450
448 451 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
449 452 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
450 453
451 454 def _runcommand(ui, options, cmd, cmdfunc):
452 455 def checkargs():
453 456 try:
454 457 return cmdfunc()
455 458 except error.SignatureError:
456 459 raise error.ParseError(cmd, _("invalid arguments"))
457 460
458 461 if options['profile']:
459 462 format = ui.config('profiling', 'format', default='text')
460 463
461 464 if not format in ['text', 'kcachegrind']:
462 465 ui.warn(_("unrecognized profiling format '%s'"
463 466 " - Ignored\n") % format)
464 467 format = 'text'
465 468
466 469 output = ui.config('profiling', 'output')
467 470
468 471 if output:
469 472 path = os.path.expanduser(output)
470 473 path = ui.expandpath(path)
471 474 ostream = open(path, 'wb')
472 475 else:
473 476 ostream = sys.stderr
474 477
475 478 try:
476 479 from mercurial import lsprof
477 480 except ImportError:
478 481 raise util.Abort(_(
479 482 'lsprof not available - install from '
480 483 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
481 484 p = lsprof.Profiler()
482 485 p.enable(subcalls=True)
483 486 try:
484 487 return checkargs()
485 488 finally:
486 489 p.disable()
487 490
488 491 if format == 'kcachegrind':
489 492 import lsprofcalltree
490 493 calltree = lsprofcalltree.KCacheGrind(p)
491 494 calltree.output(ostream)
492 495 else:
493 496 # format == 'text'
494 497 stats = lsprof.Stats(p.getstats())
495 498 stats.sort()
496 499 stats.pprint(top=10, file=ostream, climit=5)
497 500
498 501 if output:
499 502 ostream.close()
500 503 else:
501 504 return checkargs()
@@ -1,178 +1,181
1 1 # extensions.py - extension handling for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 import imp, os
9 9 import util, cmdutil, help
10 10 from i18n import _, gettext
11 11
12 12 _extensions = {}
13 13 _order = []
14 14
15 15 def extensions():
16 16 for name in _order:
17 17 module = _extensions[name]
18 18 if module:
19 19 yield name, module
20 20
21 21 def find(name):
22 22 '''return module with given extension name'''
23 23 try:
24 24 return _extensions[name]
25 25 except KeyError:
26 26 for k, v in _extensions.iteritems():
27 27 if k.endswith('.' + name) or k.endswith('/' + name):
28 28 return v
29 29 raise KeyError(name)
30 30
31 31 def loadpath(path, module_name):
32 32 module_name = module_name.replace('.', '_')
33 33 path = os.path.expanduser(path)
34 34 if os.path.isdir(path):
35 35 # module/__init__.py style
36 36 d, f = os.path.split(path.rstrip('/'))
37 37 fd, fpath, desc = imp.find_module(f, [d])
38 38 return imp.load_module(module_name, fd, fpath, desc)
39 39 else:
40 40 return imp.load_source(module_name, path)
41 41
42 42 def load(ui, name, path):
43 # unused ui argument kept for backwards compatibility
43 44 if name.startswith('hgext.') or name.startswith('hgext/'):
44 45 shortname = name[6:]
45 46 else:
46 47 shortname = name
47 48 if shortname in _extensions:
48 49 return
49 50 _extensions[shortname] = None
50 51 if path:
51 52 # the module will be loaded in sys.modules
52 53 # choose an unique name so that it doesn't
53 54 # conflicts with other modules
54 55 mod = loadpath(path, 'hgext.%s' % name)
55 56 else:
56 57 def importh(name):
57 58 mod = __import__(name)
58 59 components = name.split('.')
59 60 for comp in components[1:]:
60 61 mod = getattr(mod, comp)
61 62 return mod
62 63 try:
63 64 mod = importh("hgext.%s" % name)
64 65 except ImportError:
65 66 mod = importh(name)
66 67 _extensions[shortname] = mod
67 68 _order.append(shortname)
68 69
69 uisetup = getattr(mod, 'uisetup', None)
70 if uisetup:
71 uisetup(ui)
72
73 70 def loadall(ui):
74 71 result = ui.configitems("extensions")
72 newindex = len(_order)
75 73 for (name, path) in result:
76 74 if path:
77 75 if path[0] == '!':
78 76 continue
79 77 try:
80 78 load(ui, name, path)
81 79 except KeyboardInterrupt:
82 80 raise
83 81 except Exception, inst:
84 82 if path:
85 83 ui.warn(_("*** failed to import extension %s from %s: %s\n")
86 84 % (name, path, inst))
87 85 else:
88 86 ui.warn(_("*** failed to import extension %s: %s\n")
89 87 % (name, inst))
90 88 if ui.traceback():
91 89 return 1
92 90
91 for name in _order[newindex:]:
92 uisetup = getattr(_extensions[name], 'uisetup', None)
93 if uisetup:
94 uisetup(ui)
95
93 96 def wrapcommand(table, command, wrapper):
94 97 aliases, entry = cmdutil.findcmd(command, table)
95 98 for alias, e in table.iteritems():
96 99 if e is entry:
97 100 key = alias
98 101 break
99 102
100 103 origfn = entry[0]
101 104 def wrap(*args, **kwargs):
102 105 return util.checksignature(wrapper)(
103 106 util.checksignature(origfn), *args, **kwargs)
104 107
105 108 wrap.__doc__ = getattr(origfn, '__doc__')
106 109 wrap.__module__ = getattr(origfn, '__module__')
107 110
108 111 newentry = list(entry)
109 112 newentry[0] = wrap
110 113 table[key] = tuple(newentry)
111 114 return entry
112 115
113 116 def wrapfunction(container, funcname, wrapper):
114 117 def wrap(*args, **kwargs):
115 118 return wrapper(origfn, *args, **kwargs)
116 119
117 120 origfn = getattr(container, funcname)
118 121 setattr(container, funcname, wrap)
119 122 return origfn
120 123
121 124 def disabled():
122 125 '''find disabled extensions from hgext
123 126 returns a dict of {name: desc}, and the max name length'''
124 127
125 128 import hgext
126 129 extpath = os.path.dirname(os.path.abspath(hgext.__file__))
127 130
128 131 try: # might not be a filesystem path
129 132 files = os.listdir(extpath)
130 133 except OSError:
131 134 return None, 0
132 135
133 136 exts = {}
134 137 maxlength = 0
135 138 for e in files:
136 139
137 140 if e.endswith('.py'):
138 141 name = e.rsplit('.', 1)[0]
139 142 path = os.path.join(extpath, e)
140 143 else:
141 144 name = e
142 145 path = os.path.join(extpath, e, '__init__.py')
143 146 if not os.path.exists(path):
144 147 continue
145 148
146 149 if name in exts or name in _order or name == '__init__':
147 150 continue
148 151
149 152 try:
150 153 file = open(path)
151 154 except IOError:
152 155 continue
153 156 else:
154 157 doc = help.moduledoc(file)
155 158 file.close()
156 159
157 160 if doc: # extracting localized synopsis
158 161 exts[name] = gettext(doc).splitlines()[0]
159 162 else:
160 163 exts[name] = _('(no help text available)')
161 164
162 165 if len(name) > maxlength:
163 166 maxlength = len(name)
164 167
165 168 return exts, maxlength
166 169
167 170 def enabled():
168 171 '''return a dict of {name: desc} of extensions, and the max name length'''
169 172 exts = {}
170 173 maxlength = 0
171 174 exthelps = []
172 175 for ename, ext in extensions():
173 176 doc = (gettext(ext.__doc__) or _('(no help text available)'))
174 177 ename = ename.split('.')[-1]
175 178 maxlength = max(len(ename), maxlength)
176 179 exts[ename] = doc.splitlines()[0].strip()
177 180
178 181 return exts, maxlength
@@ -1,118 +1,141
1 1 #!/bin/sh
2 2 # Test basic extension support
3 3
4 4 "$TESTDIR/hghave" no-outer-repo || exit 80
5 5
6 6 cat > foobar.py <<EOF
7 7 import os
8 8 from mercurial import commands
9 9
10 10 def uisetup(ui):
11 11 ui.write("uisetup called\\n")
12 12
13 13 def reposetup(ui, repo):
14 14 ui.write("reposetup called for %s\\n" % os.path.basename(repo.root))
15 15 ui.write("ui %s= repo.ui\\n" % (ui == repo.ui and "=" or "!"))
16 16
17 17 def foo(ui, *args, **kwargs):
18 18 ui.write("Foo\\n")
19 19
20 20 def bar(ui, *args, **kwargs):
21 21 ui.write("Bar\\n")
22 22
23 23 cmdtable = {
24 24 "foo": (foo, [], "hg foo"),
25 25 "bar": (bar, [], "hg bar"),
26 26 }
27 27
28 28 commands.norepo += ' bar'
29 29 EOF
30 30 abspath=`pwd`/foobar.py
31 31
32 32 mkdir barfoo
33 33 cp foobar.py barfoo/__init__.py
34 34 barfoopath=`pwd`/barfoo
35 35
36 36 hg init a
37 37 cd a
38 38 echo foo > file
39 39 hg add file
40 40 hg commit -m 'add file'
41 41
42 42 echo '[extensions]' >> $HGRCPATH
43 43 echo "foobar = $abspath" >> $HGRCPATH
44 44 hg foo
45 45
46 46 cd ..
47 47 hg clone a b
48 48
49 49 hg bar
50 50 echo 'foobar = !' >> $HGRCPATH
51 51
52 52 echo '% module/__init__.py-style'
53 53 echo "barfoo = $barfoopath" >> $HGRCPATH
54 54 cd a
55 55 hg foo
56 56 echo 'barfoo = !' >> $HGRCPATH
57 57
58 # check that extensions are loaded in phases
59 cat > foo.py <<EOF
60 import os
61 name = os.path.basename(__file__).rsplit('.', 1)[0]
62 print "1) %s imported" % name
63 def uisetup(ui):
64 print "2) %s uisetup" % name
65 def extsetup():
66 print "3) %s extsetup" % name
67 def reposetup(ui, repo):
68 print "4) %s reposetup" % name
69 EOF
70
71 cp foo.py bar.py
72 echo 'foo = foo.py' >> $HGRCPATH
73 echo 'bar = bar.py' >> $HGRCPATH
74
75 # command with no output, we just want to see the extensions loaded
76 hg paths
77
78 echo 'foo = !' >> $HGRCPATH
79 echo 'bar = !' >> $HGRCPATH
80
58 81 cd ..
59 82 cat > empty.py <<EOF
60 83 '''empty cmdtable
61 84 '''
62 85 cmdtable = {}
63 86 EOF
64 87 emptypath=`pwd`/empty.py
65 88 echo "empty = $emptypath" >> $HGRCPATH
66 89 hg help empty
67 90 echo 'empty = !' >> $HGRCPATH
68 91
69 92 cat > debugextension.py <<EOF
70 93 '''only debugcommands
71 94 '''
72 95 def debugfoobar(ui, repo, *args, **opts):
73 96 "yet another debug command"
74 97 pass
75 98
76 99 def foo(ui, repo, *args, **opts):
77 100 """yet another foo command
78 101
79 102 This command has been DEPRECATED since forever.
80 103 """
81 104 pass
82 105
83 106 cmdtable = {
84 107 "debugfoobar": (debugfoobar, (), "hg debugfoobar"),
85 108 "foo": (foo, (), "hg foo")
86 109 }
87 110 EOF
88 111 debugpath=`pwd`/debugextension.py
89 112 echo "debugextension = $debugpath" >> $HGRCPATH
90 113 echo "% hg help"
91 114 hg help debugextension
92 115 echo "% hg help --verbose"
93 116 hg --verbose help debugextension
94 117 echo "% hg help --debug"
95 118 hg --debug help debugextension
96 119 echo 'debugextension = !' >> $HGRCPATH
97 120
98 121 echo % issue811
99 122 debugpath=`pwd`/debugissue811.py
100 123 cat > debugissue811.py <<EOF
101 124 '''show all loaded extensions
102 125 '''
103 126 from mercurial import extensions, commands
104 127
105 128 def debugextensions(ui):
106 129 "yet another debug command"
107 130 ui.write("%s\n" % '\n'.join([x for x, y in extensions.extensions()]))
108 131
109 132 cmdtable = {"debugextensions": (debugextensions, (), "hg debugextensions")}
110 133 commands.norepo += " debugextensions"
111 134 EOF
112 135 echo "debugissue811 = $debugpath" >> $HGRCPATH
113 136 echo "mq=" >> $HGRCPATH
114 137 echo "hgext.mq=" >> $HGRCPATH
115 138 echo "hgext/mq=" >> $HGRCPATH
116 139
117 140 echo % show extensions
118 141 hg debugextensions
@@ -1,87 +1,95
1 1 uisetup called
2 2 reposetup called for a
3 3 ui == repo.ui
4 4 Foo
5 5 uisetup called
6 6 reposetup called for a
7 7 ui == repo.ui
8 8 reposetup called for b
9 9 ui == repo.ui
10 10 updating working directory
11 11 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
12 12 uisetup called
13 13 Bar
14 14 % module/__init__.py-style
15 15 uisetup called
16 16 reposetup called for a
17 17 ui == repo.ui
18 18 Foo
19 1) foo imported
20 1) bar imported
21 2) foo uisetup
22 2) bar uisetup
23 3) foo extsetup
24 3) bar extsetup
25 4) foo reposetup
26 4) bar reposetup
19 27 empty extension - empty cmdtable
20 28
21 29 no commands defined
22 30 % hg help
23 31 debugextension extension - only debugcommands
24 32
25 33 no commands defined
26 34 % hg help --verbose
27 35 debugextension extension - only debugcommands
28 36
29 37 list of commands:
30 38
31 39 foo:
32 40 yet another foo command
33 41
34 42 enabled extensions:
35 43
36 44 debugextension only debugcommands
37 45
38 46 global options:
39 47 -R --repository repository root directory or symbolic path name
40 48 --cwd change working directory
41 49 -y --noninteractive do not prompt, assume 'yes' for any required answers
42 50 -q --quiet suppress output
43 51 -v --verbose enable additional output
44 52 --config set/override config option
45 53 --debug enable debugging output
46 54 --debugger start debugger
47 55 --encoding set the charset encoding (default: ascii)
48 56 --encodingmode set the charset encoding mode (default: strict)
49 57 --traceback print traceback on exception
50 58 --time time how long the command takes
51 59 --profile print command execution profile
52 60 --version output version information and exit
53 61 -h --help display help and exit
54 62 % hg help --debug
55 63 debugextension extension - only debugcommands
56 64
57 65 list of commands:
58 66
59 67 debugfoobar:
60 68 yet another debug command
61 69 foo:
62 70 yet another foo command
63 71
64 72 enabled extensions:
65 73
66 74 debugextension only debugcommands
67 75
68 76 global options:
69 77 -R --repository repository root directory or symbolic path name
70 78 --cwd change working directory
71 79 -y --noninteractive do not prompt, assume 'yes' for any required answers
72 80 -q --quiet suppress output
73 81 -v --verbose enable additional output
74 82 --config set/override config option
75 83 --debug enable debugging output
76 84 --debugger start debugger
77 85 --encoding set the charset encoding (default: ascii)
78 86 --encodingmode set the charset encoding mode (default: strict)
79 87 --traceback print traceback on exception
80 88 --time time how long the command takes
81 89 --profile print command execution profile
82 90 --version output version information and exit
83 91 -h --help display help and exit
84 92 % issue811
85 93 % show extensions
86 94 debugissue811
87 95 mq
General Comments 0
You need to be logged in to leave comments. Login now