##// END OF EJS Templates
cleanups: undefined variables
Dirkjan Ochtman -
r11305:d4cafcb6 default
parent child Browse files
Show More
@@ -1,531 +1,531 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 i18n import _
8 from i18n import _
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time
10 import util, commands, hg, fancyopts, extensions, hook, error
10 import util, commands, hg, fancyopts, extensions, hook, error
11 import cmdutil, encoding
11 import cmdutil, encoding
12 import ui as uimod
12 import ui as uimod
13
13
14 def run():
14 def run():
15 "run the command in sys.argv"
15 "run the command in sys.argv"
16 sys.exit(dispatch(sys.argv[1:]))
16 sys.exit(dispatch(sys.argv[1:]))
17
17
18 def dispatch(args):
18 def dispatch(args):
19 "run the command specified in args"
19 "run the command specified in args"
20 try:
20 try:
21 u = uimod.ui()
21 u = uimod.ui()
22 if '--traceback' in args:
22 if '--traceback' in args:
23 u.setconfig('ui', 'traceback', 'on')
23 u.setconfig('ui', 'traceback', 'on')
24 except util.Abort, inst:
24 except util.Abort, inst:
25 sys.stderr.write(_("abort: %s\n") % inst)
25 sys.stderr.write(_("abort: %s\n") % inst)
26 return -1
26 return -1
27 except error.ParseError, inst:
27 except error.ParseError, inst:
28 if len(inst.args) > 1:
28 if len(inst.args) > 1:
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
29 sys.stderr.write(_("hg: parse error at %s: %s\n") %
30 (inst.args[1], inst.args[0]))
30 (inst.args[1], inst.args[0]))
31 else:
31 else:
32 sys.stderr.write(_("hg: parse error: %s\n") % ints.args[0])
32 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
33 return -1
33 return -1
34 return _runcatch(u, args)
34 return _runcatch(u, args)
35
35
36 def _runcatch(ui, args):
36 def _runcatch(ui, args):
37 def catchterm(*args):
37 def catchterm(*args):
38 raise error.SignalInterrupt
38 raise error.SignalInterrupt
39
39
40 try:
40 try:
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
41 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
42 num = getattr(signal, name, None)
42 num = getattr(signal, name, None)
43 if num:
43 if num:
44 signal.signal(num, catchterm)
44 signal.signal(num, catchterm)
45 except ValueError:
45 except ValueError:
46 pass # happens if called in a thread
46 pass # happens if called in a thread
47
47
48 try:
48 try:
49 try:
49 try:
50 # enter the debugger before command execution
50 # enter the debugger before command execution
51 if '--debugger' in args:
51 if '--debugger' in args:
52 pdb.set_trace()
52 pdb.set_trace()
53 try:
53 try:
54 return _dispatch(ui, args)
54 return _dispatch(ui, args)
55 finally:
55 finally:
56 ui.flush()
56 ui.flush()
57 except:
57 except:
58 # enter the debugger when we hit an exception
58 # enter the debugger when we hit an exception
59 if '--debugger' in args:
59 if '--debugger' in args:
60 pdb.post_mortem(sys.exc_info()[2])
60 pdb.post_mortem(sys.exc_info()[2])
61 ui.traceback()
61 ui.traceback()
62 raise
62 raise
63
63
64 # Global exception handling, alphabetically
64 # Global exception handling, alphabetically
65 # Mercurial-specific first, followed by built-in and library exceptions
65 # Mercurial-specific first, followed by built-in and library exceptions
66 except error.AmbiguousCommand, inst:
66 except error.AmbiguousCommand, inst:
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
67 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
68 (inst.args[0], " ".join(inst.args[1])))
68 (inst.args[0], " ".join(inst.args[1])))
69 except error.ParseError, inst:
69 except error.ParseError, inst:
70 if len(inst.args) > 1:
70 if len(inst.args) > 1:
71 ui.warn(_("hg: parse error at %s: %s\n") %
71 ui.warn(_("hg: parse error at %s: %s\n") %
72 (inst.args[1], inst.args[0]))
72 (inst.args[1], inst.args[0]))
73 else:
73 else:
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
74 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
75 return -1
75 return -1
76 except error.LockHeld, inst:
76 except error.LockHeld, inst:
77 if inst.errno == errno.ETIMEDOUT:
77 if inst.errno == errno.ETIMEDOUT:
78 reason = _('timed out waiting for lock held by %s') % inst.locker
78 reason = _('timed out waiting for lock held by %s') % inst.locker
79 else:
79 else:
80 reason = _('lock held by %s') % inst.locker
80 reason = _('lock held by %s') % inst.locker
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
81 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
82 except error.LockUnavailable, inst:
82 except error.LockUnavailable, inst:
83 ui.warn(_("abort: could not lock %s: %s\n") %
83 ui.warn(_("abort: could not lock %s: %s\n") %
84 (inst.desc or inst.filename, inst.strerror))
84 (inst.desc or inst.filename, inst.strerror))
85 except error.CommandError, inst:
85 except error.CommandError, inst:
86 if inst.args[0]:
86 if inst.args[0]:
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
87 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
88 commands.help_(ui, inst.args[0])
88 commands.help_(ui, inst.args[0])
89 else:
89 else:
90 ui.warn(_("hg: %s\n") % inst.args[1])
90 ui.warn(_("hg: %s\n") % inst.args[1])
91 commands.help_(ui, 'shortlist')
91 commands.help_(ui, 'shortlist')
92 except error.RepoError, inst:
92 except error.RepoError, inst:
93 ui.warn(_("abort: %s!\n") % inst)
93 ui.warn(_("abort: %s!\n") % inst)
94 except error.ResponseError, inst:
94 except error.ResponseError, inst:
95 ui.warn(_("abort: %s") % inst.args[0])
95 ui.warn(_("abort: %s") % inst.args[0])
96 if not isinstance(inst.args[1], basestring):
96 if not isinstance(inst.args[1], basestring):
97 ui.warn(" %r\n" % (inst.args[1],))
97 ui.warn(" %r\n" % (inst.args[1],))
98 elif not inst.args[1]:
98 elif not inst.args[1]:
99 ui.warn(_(" empty string\n"))
99 ui.warn(_(" empty string\n"))
100 else:
100 else:
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
101 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
102 except error.RevlogError, inst:
102 except error.RevlogError, inst:
103 ui.warn(_("abort: %s!\n") % inst)
103 ui.warn(_("abort: %s!\n") % inst)
104 except error.SignalInterrupt:
104 except error.SignalInterrupt:
105 ui.warn(_("killed!\n"))
105 ui.warn(_("killed!\n"))
106 except error.UnknownCommand, inst:
106 except error.UnknownCommand, inst:
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
107 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
108 try:
108 try:
109 # check if the command is in a disabled extension
109 # check if the command is in a disabled extension
110 # (but don't check for extensions themselves)
110 # (but don't check for extensions themselves)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
111 commands.help_(ui, inst.args[0], unknowncmd=True)
112 except error.UnknownCommand:
112 except error.UnknownCommand:
113 commands.help_(ui, 'shortlist')
113 commands.help_(ui, 'shortlist')
114 except util.Abort, inst:
114 except util.Abort, inst:
115 ui.warn(_("abort: %s\n") % inst)
115 ui.warn(_("abort: %s\n") % inst)
116 except ImportError, inst:
116 except ImportError, inst:
117 ui.warn(_("abort: %s!\n") % inst)
117 ui.warn(_("abort: %s!\n") % inst)
118 m = str(inst).split()[-1]
118 m = str(inst).split()[-1]
119 if m in "mpatch bdiff".split():
119 if m in "mpatch bdiff".split():
120 ui.warn(_("(did you forget to compile extensions?)\n"))
120 ui.warn(_("(did you forget to compile extensions?)\n"))
121 elif m in "zlib".split():
121 elif m in "zlib".split():
122 ui.warn(_("(is your Python install correct?)\n"))
122 ui.warn(_("(is your Python install correct?)\n"))
123 except IOError, inst:
123 except IOError, inst:
124 if hasattr(inst, "code"):
124 if hasattr(inst, "code"):
125 ui.warn(_("abort: %s\n") % inst)
125 ui.warn(_("abort: %s\n") % inst)
126 elif hasattr(inst, "reason"):
126 elif hasattr(inst, "reason"):
127 try: # usually it is in the form (errno, strerror)
127 try: # usually it is in the form (errno, strerror)
128 reason = inst.reason.args[1]
128 reason = inst.reason.args[1]
129 except: # it might be anything, for example a string
129 except: # it might be anything, for example a string
130 reason = inst.reason
130 reason = inst.reason
131 ui.warn(_("abort: error: %s\n") % reason)
131 ui.warn(_("abort: error: %s\n") % reason)
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
132 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
133 if ui.debugflag:
133 if ui.debugflag:
134 ui.warn(_("broken pipe\n"))
134 ui.warn(_("broken pipe\n"))
135 elif getattr(inst, "strerror", None):
135 elif getattr(inst, "strerror", None):
136 if getattr(inst, "filename", None):
136 if getattr(inst, "filename", None):
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
137 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
138 else:
138 else:
139 ui.warn(_("abort: %s\n") % inst.strerror)
139 ui.warn(_("abort: %s\n") % inst.strerror)
140 else:
140 else:
141 raise
141 raise
142 except OSError, inst:
142 except OSError, inst:
143 if getattr(inst, "filename", None):
143 if getattr(inst, "filename", None):
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
145 else:
145 else:
146 ui.warn(_("abort: %s\n") % inst.strerror)
146 ui.warn(_("abort: %s\n") % inst.strerror)
147 except KeyboardInterrupt:
147 except KeyboardInterrupt:
148 try:
148 try:
149 ui.warn(_("interrupted!\n"))
149 ui.warn(_("interrupted!\n"))
150 except IOError, inst:
150 except IOError, inst:
151 if inst.errno == errno.EPIPE:
151 if inst.errno == errno.EPIPE:
152 if ui.debugflag:
152 if ui.debugflag:
153 ui.warn(_("\nbroken pipe\n"))
153 ui.warn(_("\nbroken pipe\n"))
154 else:
154 else:
155 raise
155 raise
156 except MemoryError:
156 except MemoryError:
157 ui.warn(_("abort: out of memory\n"))
157 ui.warn(_("abort: out of memory\n"))
158 except SystemExit, inst:
158 except SystemExit, inst:
159 # Commands shouldn't sys.exit directly, but give a return code.
159 # Commands shouldn't sys.exit directly, but give a return code.
160 # Just in case catch this and and pass exit code to caller.
160 # Just in case catch this and and pass exit code to caller.
161 return inst.code
161 return inst.code
162 except socket.error, inst:
162 except socket.error, inst:
163 ui.warn(_("abort: %s\n") % inst.args[-1])
163 ui.warn(_("abort: %s\n") % inst.args[-1])
164 except:
164 except:
165 ui.warn(_("** unknown exception encountered, details follow\n"))
165 ui.warn(_("** unknown exception encountered, details follow\n"))
166 ui.warn(_("** report bug details to "
166 ui.warn(_("** report bug details to "
167 "http://mercurial.selenic.com/bts/\n"))
167 "http://mercurial.selenic.com/bts/\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
168 ui.warn(_("** or mercurial@selenic.com\n"))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
169 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
170 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
171 % util.version())
171 % util.version())
172 ui.warn(_("** Extensions loaded: %s\n")
172 ui.warn(_("** Extensions loaded: %s\n")
173 % ", ".join([x[0] for x in extensions.extensions()]))
173 % ", ".join([x[0] for x in extensions.extensions()]))
174 raise
174 raise
175
175
176 return -1
176 return -1
177
177
178 def aliasargs(fn):
178 def aliasargs(fn):
179 if hasattr(fn, 'args'):
179 if hasattr(fn, 'args'):
180 return fn.args
180 return fn.args
181 return []
181 return []
182
182
183 class cmdalias(object):
183 class cmdalias(object):
184 def __init__(self, name, definition, cmdtable):
184 def __init__(self, name, definition, cmdtable):
185 self.name = name
185 self.name = name
186 self.definition = definition
186 self.definition = definition
187 self.args = []
187 self.args = []
188 self.opts = []
188 self.opts = []
189 self.help = ''
189 self.help = ''
190 self.norepo = True
190 self.norepo = True
191 self.badalias = False
191 self.badalias = False
192
192
193 try:
193 try:
194 cmdutil.findcmd(self.name, cmdtable, True)
194 cmdutil.findcmd(self.name, cmdtable, True)
195 self.shadows = True
195 self.shadows = True
196 except error.UnknownCommand:
196 except error.UnknownCommand:
197 self.shadows = False
197 self.shadows = False
198
198
199 if not self.definition:
199 if not self.definition:
200 def fn(ui, *args):
200 def fn(ui, *args):
201 ui.warn(_("no definition for alias '%s'\n") % self.name)
201 ui.warn(_("no definition for alias '%s'\n") % self.name)
202 return 1
202 return 1
203 self.fn = fn
203 self.fn = fn
204 self.badalias = True
204 self.badalias = True
205
205
206 return
206 return
207
207
208 args = shlex.split(self.definition)
208 args = shlex.split(self.definition)
209 cmd = args.pop(0)
209 cmd = args.pop(0)
210 args = map(util.expandpath, args)
210 args = map(util.expandpath, args)
211
211
212 try:
212 try:
213 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
213 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
214 if len(tableentry) > 2:
214 if len(tableentry) > 2:
215 self.fn, self.opts, self.help = tableentry
215 self.fn, self.opts, self.help = tableentry
216 else:
216 else:
217 self.fn, self.opts = tableentry
217 self.fn, self.opts = tableentry
218
218
219 self.args = aliasargs(self.fn) + args
219 self.args = aliasargs(self.fn) + args
220 if cmd not in commands.norepo.split(' '):
220 if cmd not in commands.norepo.split(' '):
221 self.norepo = False
221 self.norepo = False
222 if self.help.startswith("hg " + cmd):
222 if self.help.startswith("hg " + cmd):
223 # drop prefix in old-style help lines so hg shows the alias
223 # drop prefix in old-style help lines so hg shows the alias
224 self.help = self.help[4 + len(cmd):]
224 self.help = self.help[4 + len(cmd):]
225 self.__doc__ = self.fn.__doc__
225 self.__doc__ = self.fn.__doc__
226
226
227 except error.UnknownCommand:
227 except error.UnknownCommand:
228 def fn(ui, *args):
228 def fn(ui, *args):
229 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
229 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
230 % (self.name, cmd))
230 % (self.name, cmd))
231 try:
231 try:
232 # check if the command is in a disabled extension
232 # check if the command is in a disabled extension
233 commands.help_(ui, cmd, unknowncmd=True)
233 commands.help_(ui, cmd, unknowncmd=True)
234 except error.UnknownCommand:
234 except error.UnknownCommand:
235 pass
235 pass
236 return 1
236 return 1
237 self.fn = fn
237 self.fn = fn
238 self.badalias = True
238 self.badalias = True
239 except error.AmbiguousCommand:
239 except error.AmbiguousCommand:
240 def fn(ui, *args):
240 def fn(ui, *args):
241 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
241 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
242 % (self.name, cmd))
242 % (self.name, cmd))
243 return 1
243 return 1
244 self.fn = fn
244 self.fn = fn
245 self.badalias = True
245 self.badalias = True
246
246
247 def __call__(self, ui, *args, **opts):
247 def __call__(self, ui, *args, **opts):
248 if self.shadows:
248 if self.shadows:
249 ui.debug("alias '%s' shadows command\n" % self.name)
249 ui.debug("alias '%s' shadows command\n" % self.name)
250
250
251 return self.fn(ui, *args, **opts)
251 return self.fn(ui, *args, **opts)
252
252
253 def addaliases(ui, cmdtable):
253 def addaliases(ui, cmdtable):
254 # aliases are processed after extensions have been loaded, so they
254 # aliases are processed after extensions have been loaded, so they
255 # may use extension commands. Aliases can also use other alias definitions,
255 # may use extension commands. Aliases can also use other alias definitions,
256 # but only if they have been defined prior to the current definition.
256 # but only if they have been defined prior to the current definition.
257 for alias, definition in ui.configitems('alias'):
257 for alias, definition in ui.configitems('alias'):
258 aliasdef = cmdalias(alias, definition, cmdtable)
258 aliasdef = cmdalias(alias, definition, cmdtable)
259 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
259 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
260 if aliasdef.norepo:
260 if aliasdef.norepo:
261 commands.norepo += ' %s' % alias
261 commands.norepo += ' %s' % alias
262
262
263 def _parse(ui, args):
263 def _parse(ui, args):
264 options = {}
264 options = {}
265 cmdoptions = {}
265 cmdoptions = {}
266
266
267 try:
267 try:
268 args = fancyopts.fancyopts(args, commands.globalopts, options)
268 args = fancyopts.fancyopts(args, commands.globalopts, options)
269 except fancyopts.getopt.GetoptError, inst:
269 except fancyopts.getopt.GetoptError, inst:
270 raise error.CommandError(None, inst)
270 raise error.CommandError(None, inst)
271
271
272 if args:
272 if args:
273 cmd, args = args[0], args[1:]
273 cmd, args = args[0], args[1:]
274 aliases, entry = cmdutil.findcmd(cmd, commands.table,
274 aliases, entry = cmdutil.findcmd(cmd, commands.table,
275 ui.config("ui", "strict"))
275 ui.config("ui", "strict"))
276 cmd = aliases[0]
276 cmd = aliases[0]
277 args = aliasargs(entry[0]) + args
277 args = aliasargs(entry[0]) + args
278 defaults = ui.config("defaults", cmd)
278 defaults = ui.config("defaults", cmd)
279 if defaults:
279 if defaults:
280 args = map(util.expandpath, shlex.split(defaults)) + args
280 args = map(util.expandpath, shlex.split(defaults)) + args
281 c = list(entry[1])
281 c = list(entry[1])
282 else:
282 else:
283 cmd = None
283 cmd = None
284 c = []
284 c = []
285
285
286 # combine global options into local
286 # combine global options into local
287 for o in commands.globalopts:
287 for o in commands.globalopts:
288 c.append((o[0], o[1], options[o[1]], o[3]))
288 c.append((o[0], o[1], options[o[1]], o[3]))
289
289
290 try:
290 try:
291 args = fancyopts.fancyopts(args, c, cmdoptions, True)
291 args = fancyopts.fancyopts(args, c, cmdoptions, True)
292 except fancyopts.getopt.GetoptError, inst:
292 except fancyopts.getopt.GetoptError, inst:
293 raise error.CommandError(cmd, inst)
293 raise error.CommandError(cmd, inst)
294
294
295 # separate global options back out
295 # separate global options back out
296 for o in commands.globalopts:
296 for o in commands.globalopts:
297 n = o[1]
297 n = o[1]
298 options[n] = cmdoptions[n]
298 options[n] = cmdoptions[n]
299 del cmdoptions[n]
299 del cmdoptions[n]
300
300
301 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
301 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
302
302
303 def _parseconfig(ui, config):
303 def _parseconfig(ui, config):
304 """parse the --config options from the command line"""
304 """parse the --config options from the command line"""
305 for cfg in config:
305 for cfg in config:
306 try:
306 try:
307 name, value = cfg.split('=', 1)
307 name, value = cfg.split('=', 1)
308 section, name = name.split('.', 1)
308 section, name = name.split('.', 1)
309 if not section or not name:
309 if not section or not name:
310 raise IndexError
310 raise IndexError
311 ui.setconfig(section, name, value)
311 ui.setconfig(section, name, value)
312 except (IndexError, ValueError):
312 except (IndexError, ValueError):
313 raise util.Abort(_('malformed --config option: %r '
313 raise util.Abort(_('malformed --config option: %r '
314 '(use --config section.name=value)') % cfg)
314 '(use --config section.name=value)') % cfg)
315
315
316 def _earlygetopt(aliases, args):
316 def _earlygetopt(aliases, args):
317 """Return list of values for an option (or aliases).
317 """Return list of values for an option (or aliases).
318
318
319 The values are listed in the order they appear in args.
319 The values are listed in the order they appear in args.
320 The options and values are removed from args.
320 The options and values are removed from args.
321 """
321 """
322 try:
322 try:
323 argcount = args.index("--")
323 argcount = args.index("--")
324 except ValueError:
324 except ValueError:
325 argcount = len(args)
325 argcount = len(args)
326 shortopts = [opt for opt in aliases if len(opt) == 2]
326 shortopts = [opt for opt in aliases if len(opt) == 2]
327 values = []
327 values = []
328 pos = 0
328 pos = 0
329 while pos < argcount:
329 while pos < argcount:
330 if args[pos] in aliases:
330 if args[pos] in aliases:
331 if pos + 1 >= argcount:
331 if pos + 1 >= argcount:
332 # ignore and let getopt report an error if there is no value
332 # ignore and let getopt report an error if there is no value
333 break
333 break
334 del args[pos]
334 del args[pos]
335 values.append(args.pop(pos))
335 values.append(args.pop(pos))
336 argcount -= 2
336 argcount -= 2
337 elif args[pos][:2] in shortopts:
337 elif args[pos][:2] in shortopts:
338 # short option can have no following space, e.g. hg log -Rfoo
338 # short option can have no following space, e.g. hg log -Rfoo
339 values.append(args.pop(pos)[2:])
339 values.append(args.pop(pos)[2:])
340 argcount -= 1
340 argcount -= 1
341 else:
341 else:
342 pos += 1
342 pos += 1
343 return values
343 return values
344
344
345 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
345 def runcommand(lui, repo, cmd, fullargs, ui, options, d):
346 # run pre-hook, and abort if it fails
346 # run pre-hook, and abort if it fails
347 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
347 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs))
348 if ret:
348 if ret:
349 return ret
349 return ret
350 ret = _runcommand(ui, options, cmd, d)
350 ret = _runcommand(ui, options, cmd, d)
351 # run post-hook, passing command result
351 # run post-hook, passing command result
352 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
352 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
353 result = ret)
353 result = ret)
354 return ret
354 return ret
355
355
356 _loaded = set()
356 _loaded = set()
357 def _dispatch(ui, args):
357 def _dispatch(ui, args):
358 # read --config before doing anything else
358 # read --config before doing anything else
359 # (e.g. to change trust settings for reading .hg/hgrc)
359 # (e.g. to change trust settings for reading .hg/hgrc)
360 _parseconfig(ui, _earlygetopt(['--config'], args))
360 _parseconfig(ui, _earlygetopt(['--config'], args))
361
361
362 # check for cwd
362 # check for cwd
363 cwd = _earlygetopt(['--cwd'], args)
363 cwd = _earlygetopt(['--cwd'], args)
364 if cwd:
364 if cwd:
365 os.chdir(cwd[-1])
365 os.chdir(cwd[-1])
366
366
367 # read the local repository .hgrc into a local ui object
367 # read the local repository .hgrc into a local ui object
368 path = cmdutil.findrepo(os.getcwd()) or ""
368 path = cmdutil.findrepo(os.getcwd()) or ""
369 if not path:
369 if not path:
370 lui = ui
370 lui = ui
371 else:
371 else:
372 try:
372 try:
373 lui = ui.copy()
373 lui = ui.copy()
374 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
374 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
375 except IOError:
375 except IOError:
376 pass
376 pass
377
377
378 # now we can expand paths, even ones in .hg/hgrc
378 # now we can expand paths, even ones in .hg/hgrc
379 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
379 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
380 if rpath:
380 if rpath:
381 path = lui.expandpath(rpath[-1])
381 path = lui.expandpath(rpath[-1])
382 lui = ui.copy()
382 lui = ui.copy()
383 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
383 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
384
384
385 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
385 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
386 # reposetup. Programs like TortoiseHg will call _dispatch several
386 # reposetup. Programs like TortoiseHg will call _dispatch several
387 # times so we keep track of configured extensions in _loaded.
387 # times so we keep track of configured extensions in _loaded.
388 extensions.loadall(lui)
388 extensions.loadall(lui)
389 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
389 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
390
390
391 # (uisetup and extsetup are handled in extensions.loadall)
391 # (uisetup and extsetup are handled in extensions.loadall)
392
392
393 for name, module in exts:
393 for name, module in exts:
394 cmdtable = getattr(module, 'cmdtable', {})
394 cmdtable = getattr(module, 'cmdtable', {})
395 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
395 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
396 if overrides:
396 if overrides:
397 ui.warn(_("extension '%s' overrides commands: %s\n")
397 ui.warn(_("extension '%s' overrides commands: %s\n")
398 % (name, " ".join(overrides)))
398 % (name, " ".join(overrides)))
399 commands.table.update(cmdtable)
399 commands.table.update(cmdtable)
400 _loaded.add(name)
400 _loaded.add(name)
401
401
402 # (reposetup is handled in hg.repository)
402 # (reposetup is handled in hg.repository)
403
403
404 addaliases(lui, commands.table)
404 addaliases(lui, commands.table)
405
405
406 # check for fallback encoding
406 # check for fallback encoding
407 fallback = lui.config('ui', 'fallbackencoding')
407 fallback = lui.config('ui', 'fallbackencoding')
408 if fallback:
408 if fallback:
409 encoding.fallbackencoding = fallback
409 encoding.fallbackencoding = fallback
410
410
411 fullargs = args
411 fullargs = args
412 cmd, func, args, options, cmdoptions = _parse(lui, args)
412 cmd, func, args, options, cmdoptions = _parse(lui, args)
413
413
414 if options["config"]:
414 if options["config"]:
415 raise util.Abort(_("Option --config may not be abbreviated!"))
415 raise util.Abort(_("Option --config may not be abbreviated!"))
416 if options["cwd"]:
416 if options["cwd"]:
417 raise util.Abort(_("Option --cwd may not be abbreviated!"))
417 raise util.Abort(_("Option --cwd may not be abbreviated!"))
418 if options["repository"]:
418 if options["repository"]:
419 raise util.Abort(_(
419 raise util.Abort(_(
420 "Option -R has to be separated from other options (e.g. not -qR) "
420 "Option -R has to be separated from other options (e.g. not -qR) "
421 "and --repository may only be abbreviated as --repo!"))
421 "and --repository may only be abbreviated as --repo!"))
422
422
423 if options["encoding"]:
423 if options["encoding"]:
424 encoding.encoding = options["encoding"]
424 encoding.encoding = options["encoding"]
425 if options["encodingmode"]:
425 if options["encodingmode"]:
426 encoding.encodingmode = options["encodingmode"]
426 encoding.encodingmode = options["encodingmode"]
427 if options["time"]:
427 if options["time"]:
428 def get_times():
428 def get_times():
429 t = os.times()
429 t = os.times()
430 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
430 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
431 t = (t[0], t[1], t[2], t[3], time.clock())
431 t = (t[0], t[1], t[2], t[3], time.clock())
432 return t
432 return t
433 s = get_times()
433 s = get_times()
434 def print_time():
434 def print_time():
435 t = get_times()
435 t = get_times()
436 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
436 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
437 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
437 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
438 atexit.register(print_time)
438 atexit.register(print_time)
439
439
440 if options['verbose'] or options['debug'] or options['quiet']:
440 if options['verbose'] or options['debug'] or options['quiet']:
441 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
441 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
442 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
442 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
443 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
443 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
444 if options['traceback']:
444 if options['traceback']:
445 ui.setconfig('ui', 'traceback', 'on')
445 ui.setconfig('ui', 'traceback', 'on')
446 if options['noninteractive']:
446 if options['noninteractive']:
447 ui.setconfig('ui', 'interactive', 'off')
447 ui.setconfig('ui', 'interactive', 'off')
448
448
449 if options['help']:
449 if options['help']:
450 return commands.help_(ui, cmd, options['version'])
450 return commands.help_(ui, cmd, options['version'])
451 elif options['version']:
451 elif options['version']:
452 return commands.version_(ui)
452 return commands.version_(ui)
453 elif not cmd:
453 elif not cmd:
454 return commands.help_(ui, 'shortlist')
454 return commands.help_(ui, 'shortlist')
455
455
456 repo = None
456 repo = None
457 if cmd not in commands.norepo.split():
457 if cmd not in commands.norepo.split():
458 try:
458 try:
459 repo = hg.repository(ui, path=path)
459 repo = hg.repository(ui, path=path)
460 ui = repo.ui
460 ui = repo.ui
461 if not repo.local():
461 if not repo.local():
462 raise util.Abort(_("repository '%s' is not local") % path)
462 raise util.Abort(_("repository '%s' is not local") % path)
463 ui.setconfig("bundle", "mainreporoot", repo.root)
463 ui.setconfig("bundle", "mainreporoot", repo.root)
464 except error.RepoError:
464 except error.RepoError:
465 if cmd not in commands.optionalrepo.split():
465 if cmd not in commands.optionalrepo.split():
466 if args and not path: # try to infer -R from command args
466 if args and not path: # try to infer -R from command args
467 repos = map(cmdutil.findrepo, args)
467 repos = map(cmdutil.findrepo, args)
468 guess = repos[0]
468 guess = repos[0]
469 if guess and repos.count(guess) == len(repos):
469 if guess and repos.count(guess) == len(repos):
470 return _dispatch(ui, ['--repository', guess] + fullargs)
470 return _dispatch(ui, ['--repository', guess] + fullargs)
471 if not path:
471 if not path:
472 raise error.RepoError(_("There is no Mercurial repository"
472 raise error.RepoError(_("There is no Mercurial repository"
473 " here (.hg not found)"))
473 " here (.hg not found)"))
474 raise
474 raise
475 args.insert(0, repo)
475 args.insert(0, repo)
476 elif rpath:
476 elif rpath:
477 ui.warn("warning: --repository ignored\n")
477 ui.warn("warning: --repository ignored\n")
478
478
479 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
479 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
480 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
480 return runcommand(lui, repo, cmd, fullargs, ui, options, d)
481
481
482 def _runcommand(ui, options, cmd, cmdfunc):
482 def _runcommand(ui, options, cmd, cmdfunc):
483 def checkargs():
483 def checkargs():
484 try:
484 try:
485 return cmdfunc()
485 return cmdfunc()
486 except error.SignatureError:
486 except error.SignatureError:
487 raise error.CommandError(cmd, _("invalid arguments"))
487 raise error.CommandError(cmd, _("invalid arguments"))
488
488
489 if options['profile']:
489 if options['profile']:
490 format = ui.config('profiling', 'format', default='text')
490 format = ui.config('profiling', 'format', default='text')
491
491
492 if not format in ['text', 'kcachegrind']:
492 if not format in ['text', 'kcachegrind']:
493 ui.warn(_("unrecognized profiling format '%s'"
493 ui.warn(_("unrecognized profiling format '%s'"
494 " - Ignored\n") % format)
494 " - Ignored\n") % format)
495 format = 'text'
495 format = 'text'
496
496
497 output = ui.config('profiling', 'output')
497 output = ui.config('profiling', 'output')
498
498
499 if output:
499 if output:
500 path = ui.expandpath(output)
500 path = ui.expandpath(output)
501 ostream = open(path, 'wb')
501 ostream = open(path, 'wb')
502 else:
502 else:
503 ostream = sys.stderr
503 ostream = sys.stderr
504
504
505 try:
505 try:
506 from mercurial import lsprof
506 from mercurial import lsprof
507 except ImportError:
507 except ImportError:
508 raise util.Abort(_(
508 raise util.Abort(_(
509 'lsprof not available - install from '
509 'lsprof not available - install from '
510 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
510 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
511 p = lsprof.Profiler()
511 p = lsprof.Profiler()
512 p.enable(subcalls=True)
512 p.enable(subcalls=True)
513 try:
513 try:
514 return checkargs()
514 return checkargs()
515 finally:
515 finally:
516 p.disable()
516 p.disable()
517
517
518 if format == 'kcachegrind':
518 if format == 'kcachegrind':
519 import lsprofcalltree
519 import lsprofcalltree
520 calltree = lsprofcalltree.KCacheGrind(p)
520 calltree = lsprofcalltree.KCacheGrind(p)
521 calltree.output(ostream)
521 calltree.output(ostream)
522 else:
522 else:
523 # format == 'text'
523 # format == 'text'
524 stats = lsprof.Stats(p.getstats())
524 stats = lsprof.Stats(p.getstats())
525 stats.sort()
525 stats.sort()
526 stats.pprint(top=10, file=ostream, climit=5)
526 stats.pprint(top=10, file=ostream, climit=5)
527
527
528 if output:
528 if output:
529 ostream.close()
529 ostream.close()
530 else:
530 else:
531 return checkargs()
531 return checkargs()
@@ -1,91 +1,91 b''
1 # parser.py - simple top-down operator precedence parser for mercurial
1 # parser.py - simple top-down operator precedence parser for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 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 # see http://effbot.org/zone/simple-top-down-parsing.txt and
8 # see http://effbot.org/zone/simple-top-down-parsing.txt and
9 # http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
9 # http://eli.thegreenplace.net/2010/01/02/top-down-operator-precedence-parsing/
10 # for background
10 # for background
11
11
12 # takes a tokenizer and elements
12 # takes a tokenizer and elements
13 # tokenizer is an iterator that returns type, value pairs
13 # tokenizer is an iterator that returns type, value pairs
14 # elements is a mapping of types to binding strength, prefix and infix actions
14 # elements is a mapping of types to binding strength, prefix and infix actions
15 # an action is a tree node name, a tree label, and an optional match
15 # an action is a tree node name, a tree label, and an optional match
16 # __call__(program) parses program into a labelled tree
16 # __call__(program) parses program into a labelled tree
17
17
18 import error
18 import error
19
19
20 class parser(object):
20 class parser(object):
21 def __init__(self, tokenizer, elements, methods=None):
21 def __init__(self, tokenizer, elements, methods=None):
22 self._tokenizer = tokenizer
22 self._tokenizer = tokenizer
23 self._elements = elements
23 self._elements = elements
24 self._methods = methods
24 self._methods = methods
25 def _advance(self):
25 def _advance(self):
26 'advance the tokenizer'
26 'advance the tokenizer'
27 t = self.current
27 t = self.current
28 try:
28 try:
29 self.current = self._iter.next()
29 self.current = self._iter.next()
30 except StopIteration:
30 except StopIteration:
31 pass
31 pass
32 return t
32 return t
33 def _match(self, m):
33 def _match(self, m):
34 'make sure the tokenizer matches an end condition'
34 'make sure the tokenizer matches an end condition'
35 if self.current[0] != m:
35 if self.current[0] != m:
36 raise error.ParseError("unexpected token: %s" % self.current[2],
36 raise error.ParseError("unexpected token: %s" % self.current[0],
37 pos)
37 self.current[2])
38 self._advance()
38 self._advance()
39 def _parse(self, bind=0):
39 def _parse(self, bind=0):
40 token, value, pos = self._advance()
40 token, value, pos = self._advance()
41 # handle prefix rules on current token
41 # handle prefix rules on current token
42 prefix = self._elements[token][1]
42 prefix = self._elements[token][1]
43 if not prefix:
43 if not prefix:
44 raise error.ParseError("not a prefix: %s" % token, pos)
44 raise error.ParseError("not a prefix: %s" % token, pos)
45 if len(prefix) == 1:
45 if len(prefix) == 1:
46 expr = (prefix[0], value)
46 expr = (prefix[0], value)
47 else:
47 else:
48 if len(prefix) > 2 and prefix[2] == self.current[0]:
48 if len(prefix) > 2 and prefix[2] == self.current[0]:
49 self._match(prefix[2])
49 self._match(prefix[2])
50 expr = (prefix[0], None)
50 expr = (prefix[0], None)
51 else:
51 else:
52 expr = (prefix[0], self._parse(prefix[1]))
52 expr = (prefix[0], self._parse(prefix[1]))
53 if len(prefix) > 2:
53 if len(prefix) > 2:
54 self._match(prefix[2])
54 self._match(prefix[2])
55 # gather tokens until we meet a lower binding strength
55 # gather tokens until we meet a lower binding strength
56 while bind < self._elements[self.current[0]][0]:
56 while bind < self._elements[self.current[0]][0]:
57 token, value, pos = self._advance()
57 token, value, pos = self._advance()
58 e = self._elements[token]
58 e = self._elements[token]
59 # check for suffix - next token isn't a valid prefix
59 # check for suffix - next token isn't a valid prefix
60 if len(e) == 4 and not self._elements[self.current[0]][1]:
60 if len(e) == 4 and not self._elements[self.current[0]][1]:
61 suffix = e[3]
61 suffix = e[3]
62 expr = (suffix[0], expr)
62 expr = (suffix[0], expr)
63 else:
63 else:
64 # handle infix rules
64 # handle infix rules
65 infix = self._elements[token][2]
65 infix = self._elements[token][2]
66 if len(infix) == 3 and infix[2] == self.current[0]:
66 if len(infix) == 3 and infix[2] == self.current[0]:
67 self._match(infix[2])
67 self._match(infix[2])
68 expr = (infix[0], expr, (None))
68 expr = (infix[0], expr, (None))
69 else:
69 else:
70 if not infix[0]:
70 if not infix[0]:
71 raise error.ParseError("not an infix: %s" % token, pos)
71 raise error.ParseError("not an infix: %s" % token, pos)
72 expr = (infix[0], expr, self._parse(infix[1]))
72 expr = (infix[0], expr, self._parse(infix[1]))
73 if len(infix) == 3:
73 if len(infix) == 3:
74 self._match(infix[2])
74 self._match(infix[2])
75 return expr
75 return expr
76 def parse(self, message):
76 def parse(self, message):
77 'generate a parse tree from a message'
77 'generate a parse tree from a message'
78 self._iter = self._tokenizer(message)
78 self._iter = self._tokenizer(message)
79 self.current = self._iter.next()
79 self.current = self._iter.next()
80 return self._parse()
80 return self._parse()
81 def eval(self, tree):
81 def eval(self, tree):
82 'recursively evaluate a parse tree using node methods'
82 'recursively evaluate a parse tree using node methods'
83 if not isinstance(tree, tuple):
83 if not isinstance(tree, tuple):
84 return tree
84 return tree
85 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]])
85 return self._methods[tree[0]](*[self.eval(t) for t in tree[1:]])
86 def __call__(self, message):
86 def __call__(self, message):
87 'parse a message into a parse tree and evaluate if methods given'
87 'parse a message into a parse tree and evaluate if methods given'
88 t = self.parse(message)
88 t = self.parse(message)
89 if self._methods:
89 if self._methods:
90 return self.eval(t)
90 return self.eval(t)
91 return t
91 return t
@@ -1,286 +1,286 b''
1 # templater.py - template expansion for output
1 # templater.py - template expansion for output
2 #
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005, 2006 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 i18n import _
8 from i18n import _
9 import sys, os
9 import sys, os
10 import util, config, templatefilters
10 import util, config, templatefilters
11
11
12 path = ['templates', '../templates']
12 path = ['templates', '../templates']
13 stringify = templatefilters.stringify
13 stringify = templatefilters.stringify
14
14
15 def _flatten(thing):
15 def _flatten(thing):
16 '''yield a single stream from a possibly nested set of iterators'''
16 '''yield a single stream from a possibly nested set of iterators'''
17 if isinstance(thing, str):
17 if isinstance(thing, str):
18 yield thing
18 yield thing
19 elif not hasattr(thing, '__iter__'):
19 elif not hasattr(thing, '__iter__'):
20 if i is not None:
20 if thing is not None:
21 yield str(thing)
21 yield str(thing)
22 else:
22 else:
23 for i in thing:
23 for i in thing:
24 if isinstance(i, str):
24 if isinstance(i, str):
25 yield i
25 yield i
26 elif not hasattr(i, '__iter__'):
26 elif not hasattr(i, '__iter__'):
27 if i is not None:
27 if i is not None:
28 yield str(i)
28 yield str(i)
29 elif i is not None:
29 elif i is not None:
30 for j in _flatten(i):
30 for j in _flatten(i):
31 yield j
31 yield j
32
32
33 def parsestring(s, quoted=True):
33 def parsestring(s, quoted=True):
34 '''parse a string using simple c-like syntax.
34 '''parse a string using simple c-like syntax.
35 string must be in quotes if quoted is True.'''
35 string must be in quotes if quoted is True.'''
36 if quoted:
36 if quoted:
37 if len(s) < 2 or s[0] != s[-1]:
37 if len(s) < 2 or s[0] != s[-1]:
38 raise SyntaxError(_('unmatched quotes'))
38 raise SyntaxError(_('unmatched quotes'))
39 return s[1:-1].decode('string_escape')
39 return s[1:-1].decode('string_escape')
40
40
41 return s.decode('string_escape')
41 return s.decode('string_escape')
42
42
43 class engine(object):
43 class engine(object):
44 '''template expansion engine.
44 '''template expansion engine.
45
45
46 template expansion works like this. a map file contains key=value
46 template expansion works like this. a map file contains key=value
47 pairs. if value is quoted, it is treated as string. otherwise, it
47 pairs. if value is quoted, it is treated as string. otherwise, it
48 is treated as name of template file.
48 is treated as name of template file.
49
49
50 templater is asked to expand a key in map. it looks up key, and
50 templater is asked to expand a key in map. it looks up key, and
51 looks for strings like this: {foo}. it expands {foo} by looking up
51 looks for strings like this: {foo}. it expands {foo} by looking up
52 foo in map, and substituting it. expansion is recursive: it stops
52 foo in map, and substituting it. expansion is recursive: it stops
53 when there is no more {foo} to replace.
53 when there is no more {foo} to replace.
54
54
55 expansion also allows formatting and filtering.
55 expansion also allows formatting and filtering.
56
56
57 format uses key to expand each item in list. syntax is
57 format uses key to expand each item in list. syntax is
58 {key%format}.
58 {key%format}.
59
59
60 filter uses function to transform value. syntax is
60 filter uses function to transform value. syntax is
61 {key|filter1|filter2|...}.'''
61 {key|filter1|filter2|...}.'''
62
62
63 def __init__(self, loader, filters={}, defaults={}):
63 def __init__(self, loader, filters={}, defaults={}):
64 self._loader = loader
64 self._loader = loader
65 self._filters = filters
65 self._filters = filters
66 self._defaults = defaults
66 self._defaults = defaults
67 self._cache = {}
67 self._cache = {}
68
68
69 def process(self, t, mapping):
69 def process(self, t, mapping):
70 '''Perform expansion. t is name of map element to expand.
70 '''Perform expansion. t is name of map element to expand.
71 mapping contains added elements for use during expansion. Is a
71 mapping contains added elements for use during expansion. Is a
72 generator.'''
72 generator.'''
73 return _flatten(self._process(self._load(t), mapping))
73 return _flatten(self._process(self._load(t), mapping))
74
74
75 def _load(self, t):
75 def _load(self, t):
76 '''load, parse, and cache a template'''
76 '''load, parse, and cache a template'''
77 if t not in self._cache:
77 if t not in self._cache:
78 self._cache[t] = self._parse(self._loader(t))
78 self._cache[t] = self._parse(self._loader(t))
79 return self._cache[t]
79 return self._cache[t]
80
80
81 def _get(self, mapping, key):
81 def _get(self, mapping, key):
82 v = mapping.get(key)
82 v = mapping.get(key)
83 if v is None:
83 if v is None:
84 v = self._defaults.get(key, '')
84 v = self._defaults.get(key, '')
85 if hasattr(v, '__call__'):
85 if hasattr(v, '__call__'):
86 v = v(**mapping)
86 v = v(**mapping)
87 return v
87 return v
88
88
89 def _filter(self, mapping, parts):
89 def _filter(self, mapping, parts):
90 filters, val = parts
90 filters, val = parts
91 x = self._get(mapping, val)
91 x = self._get(mapping, val)
92 for f in filters:
92 for f in filters:
93 x = f(x)
93 x = f(x)
94 return x
94 return x
95
95
96 def _format(self, mapping, args):
96 def _format(self, mapping, args):
97 key, parsed = args
97 key, parsed = args
98 v = self._get(mapping, key)
98 v = self._get(mapping, key)
99 if not hasattr(v, '__iter__'):
99 if not hasattr(v, '__iter__'):
100 raise SyntaxError(_("error expanding '%s%%%s'")
100 raise SyntaxError(_("error expanding '%s%%%s'")
101 % (key, format))
101 % (key, format))
102 lm = mapping.copy()
102 lm = mapping.copy()
103 for i in v:
103 for i in v:
104 if isinstance(i, dict):
104 if isinstance(i, dict):
105 lm.update(i)
105 lm.update(i)
106 yield self._process(parsed, lm)
106 yield self._process(parsed, lm)
107 else:
107 else:
108 # v is not an iterable of dicts, this happen when 'key'
108 # v is not an iterable of dicts, this happen when 'key'
109 # has been fully expanded already and format is useless.
109 # has been fully expanded already and format is useless.
110 # If so, return the expanded value.
110 # If so, return the expanded value.
111 yield i
111 yield i
112
112
113 def _parse(self, tmpl):
113 def _parse(self, tmpl):
114 '''preparse a template'''
114 '''preparse a template'''
115 parsed = []
115 parsed = []
116 pos, stop = 0, len(tmpl)
116 pos, stop = 0, len(tmpl)
117 while pos < stop:
117 while pos < stop:
118 n = tmpl.find('{', pos)
118 n = tmpl.find('{', pos)
119 if n < 0:
119 if n < 0:
120 parsed.append((None, tmpl[pos:stop]))
120 parsed.append((None, tmpl[pos:stop]))
121 break
121 break
122 if n > 0 and tmpl[n - 1] == '\\':
122 if n > 0 and tmpl[n - 1] == '\\':
123 # escaped
123 # escaped
124 parsed.append((None, tmpl[pos:n - 1] + "{"))
124 parsed.append((None, tmpl[pos:n - 1] + "{"))
125 pos = n + 1
125 pos = n + 1
126 continue
126 continue
127 if n > pos:
127 if n > pos:
128 parsed.append((None, tmpl[pos:n]))
128 parsed.append((None, tmpl[pos:n]))
129
129
130 pos = n
130 pos = n
131 n = tmpl.find('}', pos)
131 n = tmpl.find('}', pos)
132 if n < 0:
132 if n < 0:
133 # no closing
133 # no closing
134 parsed.append((None, tmpl[pos:stop]))
134 parsed.append((None, tmpl[pos:stop]))
135 break
135 break
136
136
137 expr = tmpl[pos + 1:n]
137 expr = tmpl[pos + 1:n]
138 pos = n + 1
138 pos = n + 1
139
139
140 if '%' in expr:
140 if '%' in expr:
141 key, t = expr.split('%')
141 key, t = expr.split('%')
142 parsed.append((self._format, (key.strip(),
142 parsed.append((self._format, (key.strip(),
143 self._load(t.strip()))))
143 self._load(t.strip()))))
144 elif '|' in expr:
144 elif '|' in expr:
145 parts = expr.split('|')
145 parts = expr.split('|')
146 val = parts[0].strip()
146 val = parts[0].strip()
147 try:
147 try:
148 filters = [self._filters[f.strip()] for f in parts[1:]]
148 filters = [self._filters[f.strip()] for f in parts[1:]]
149 except KeyError, i:
149 except KeyError, i:
150 raise SyntaxError(_("unknown filter '%s'") % i[0])
150 raise SyntaxError(_("unknown filter '%s'") % i[0])
151 parsed.append((self._filter, (filters, val)))
151 parsed.append((self._filter, (filters, val)))
152 else:
152 else:
153 parsed.append((self._get, expr.strip()))
153 parsed.append((self._get, expr.strip()))
154
154
155 return parsed
155 return parsed
156
156
157 def _process(self, parsed, mapping):
157 def _process(self, parsed, mapping):
158 '''Render a template. Returns a generator.'''
158 '''Render a template. Returns a generator.'''
159 for f, e in parsed:
159 for f, e in parsed:
160 if f:
160 if f:
161 yield f(mapping, e)
161 yield f(mapping, e)
162 else:
162 else:
163 yield e
163 yield e
164
164
165 engines = {'default': engine}
165 engines = {'default': engine}
166
166
167 class templater(object):
167 class templater(object):
168
168
169 def __init__(self, mapfile, filters={}, defaults={}, cache={},
169 def __init__(self, mapfile, filters={}, defaults={}, cache={},
170 minchunk=1024, maxchunk=65536):
170 minchunk=1024, maxchunk=65536):
171 '''set up template engine.
171 '''set up template engine.
172 mapfile is name of file to read map definitions from.
172 mapfile is name of file to read map definitions from.
173 filters is dict of functions. each transforms a value into another.
173 filters is dict of functions. each transforms a value into another.
174 defaults is dict of default map definitions.'''
174 defaults is dict of default map definitions.'''
175 self.mapfile = mapfile or 'template'
175 self.mapfile = mapfile or 'template'
176 self.cache = cache.copy()
176 self.cache = cache.copy()
177 self.map = {}
177 self.map = {}
178 self.base = (mapfile and os.path.dirname(mapfile)) or ''
178 self.base = (mapfile and os.path.dirname(mapfile)) or ''
179 self.filters = templatefilters.filters.copy()
179 self.filters = templatefilters.filters.copy()
180 self.filters.update(filters)
180 self.filters.update(filters)
181 self.defaults = defaults
181 self.defaults = defaults
182 self.minchunk, self.maxchunk = minchunk, maxchunk
182 self.minchunk, self.maxchunk = minchunk, maxchunk
183 self.engines = {}
183 self.engines = {}
184
184
185 if not mapfile:
185 if not mapfile:
186 return
186 return
187 if not os.path.exists(mapfile):
187 if not os.path.exists(mapfile):
188 raise util.Abort(_('style not found: %s') % mapfile)
188 raise util.Abort(_('style not found: %s') % mapfile)
189
189
190 conf = config.config()
190 conf = config.config()
191 conf.read(mapfile)
191 conf.read(mapfile)
192
192
193 for key, val in conf[''].items():
193 for key, val in conf[''].items():
194 if val[0] in "'\"":
194 if val[0] in "'\"":
195 try:
195 try:
196 self.cache[key] = parsestring(val)
196 self.cache[key] = parsestring(val)
197 except SyntaxError, inst:
197 except SyntaxError, inst:
198 raise SyntaxError('%s: %s' %
198 raise SyntaxError('%s: %s' %
199 (conf.source('', key), inst.args[0]))
199 (conf.source('', key), inst.args[0]))
200 else:
200 else:
201 val = 'default', val
201 val = 'default', val
202 if ':' in val[1]:
202 if ':' in val[1]:
203 val = val[1].split(':', 1)
203 val = val[1].split(':', 1)
204 self.map[key] = val[0], os.path.join(self.base, val[1])
204 self.map[key] = val[0], os.path.join(self.base, val[1])
205
205
206 def __contains__(self, key):
206 def __contains__(self, key):
207 return key in self.cache or key in self.map
207 return key in self.cache or key in self.map
208
208
209 def load(self, t):
209 def load(self, t):
210 '''Get the template for the given template name. Use a local cache.'''
210 '''Get the template for the given template name. Use a local cache.'''
211 if not t in self.cache:
211 if not t in self.cache:
212 try:
212 try:
213 self.cache[t] = open(self.map[t][1]).read()
213 self.cache[t] = open(self.map[t][1]).read()
214 except IOError, inst:
214 except IOError, inst:
215 raise IOError(inst.args[0], _('template file %s: %s') %
215 raise IOError(inst.args[0], _('template file %s: %s') %
216 (self.map[t][1], inst.args[1]))
216 (self.map[t][1], inst.args[1]))
217 return self.cache[t]
217 return self.cache[t]
218
218
219 def __call__(self, t, **mapping):
219 def __call__(self, t, **mapping):
220 ttype = t in self.map and self.map[t][0] or 'default'
220 ttype = t in self.map and self.map[t][0] or 'default'
221 proc = self.engines.get(ttype)
221 proc = self.engines.get(ttype)
222 if proc is None:
222 if proc is None:
223 proc = engines[ttype](self.load, self.filters, self.defaults)
223 proc = engines[ttype](self.load, self.filters, self.defaults)
224 self.engines[ttype] = proc
224 self.engines[ttype] = proc
225
225
226 stream = proc.process(t, mapping)
226 stream = proc.process(t, mapping)
227 if self.minchunk:
227 if self.minchunk:
228 stream = util.increasingchunks(stream, min=self.minchunk,
228 stream = util.increasingchunks(stream, min=self.minchunk,
229 max=self.maxchunk)
229 max=self.maxchunk)
230 return stream
230 return stream
231
231
232 def templatepath(name=None):
232 def templatepath(name=None):
233 '''return location of template file or directory (if no name).
233 '''return location of template file or directory (if no name).
234 returns None if not found.'''
234 returns None if not found.'''
235 normpaths = []
235 normpaths = []
236
236
237 # executable version (py2exe) doesn't support __file__
237 # executable version (py2exe) doesn't support __file__
238 if hasattr(sys, 'frozen'):
238 if hasattr(sys, 'frozen'):
239 module = sys.executable
239 module = sys.executable
240 else:
240 else:
241 module = __file__
241 module = __file__
242 for f in path:
242 for f in path:
243 if f.startswith('/'):
243 if f.startswith('/'):
244 p = f
244 p = f
245 else:
245 else:
246 fl = f.split('/')
246 fl = f.split('/')
247 p = os.path.join(os.path.dirname(module), *fl)
247 p = os.path.join(os.path.dirname(module), *fl)
248 if name:
248 if name:
249 p = os.path.join(p, name)
249 p = os.path.join(p, name)
250 if name and os.path.exists(p):
250 if name and os.path.exists(p):
251 return os.path.normpath(p)
251 return os.path.normpath(p)
252 elif os.path.isdir(p):
252 elif os.path.isdir(p):
253 normpaths.append(os.path.normpath(p))
253 normpaths.append(os.path.normpath(p))
254
254
255 return normpaths
255 return normpaths
256
256
257 def stylemap(styles, paths=None):
257 def stylemap(styles, paths=None):
258 """Return path to mapfile for a given style.
258 """Return path to mapfile for a given style.
259
259
260 Searches mapfile in the following locations:
260 Searches mapfile in the following locations:
261 1. templatepath/style/map
261 1. templatepath/style/map
262 2. templatepath/map-style
262 2. templatepath/map-style
263 3. templatepath/map
263 3. templatepath/map
264 """
264 """
265
265
266 if paths is None:
266 if paths is None:
267 paths = templatepath()
267 paths = templatepath()
268 elif isinstance(paths, str):
268 elif isinstance(paths, str):
269 paths = [paths]
269 paths = [paths]
270
270
271 if isinstance(styles, str):
271 if isinstance(styles, str):
272 styles = [styles]
272 styles = [styles]
273
273
274 for style in styles:
274 for style in styles:
275 if not style:
275 if not style:
276 continue
276 continue
277 locations = [os.path.join(style, 'map'), 'map-' + style]
277 locations = [os.path.join(style, 'map'), 'map-' + style]
278 locations.append('map')
278 locations.append('map')
279
279
280 for path in paths:
280 for path in paths:
281 for location in locations:
281 for location in locations:
282 mapfile = os.path.join(path, location)
282 mapfile = os.path.join(path, location)
283 if os.path.isfile(mapfile):
283 if os.path.isfile(mapfile):
284 return style, mapfile
284 return style, mapfile
285
285
286 raise RuntimeError("No hgweb templates found in %r" % paths)
286 raise RuntimeError("No hgweb templates found in %r" % paths)
General Comments 0
You need to be logged in to leave comments. Login now