##// END OF EJS Templates
Merge with stable
Martin Geisler -
r11601:4d9b4725 merge default
parent child Browse files
Show More
@@ -1,238 +1,238 b''
1 1 #!/usr/bin/env python
2 2 #
3 3 # check-code - a style and portability checker for Mercurial
4 4 #
5 5 # Copyright 2010 Matt Mackall <mpm@selenic.com>
6 6 #
7 7 # This software may be used and distributed according to the terms of the
8 8 # GNU General Public License version 2 or any later version.
9 9
10 10 import re, glob
11 11 import optparse
12 12
13 13 def repquote(m):
14 14 t = re.sub(r"\w", "x", m.group('text'))
15 15 t = re.sub(r"[^\sx]", "o", t)
16 16 return m.group('quote') + t + m.group('quote')
17 17
18 18 def reppython(m):
19 19 comment = m.group('comment')
20 20 if comment:
21 21 return "#" * len(comment)
22 22 return repquote(m)
23 23
24 24 def repcomment(m):
25 25 return m.group(1) + "#" * len(m.group(2))
26 26
27 27 def repccomment(m):
28 28 t = re.sub(r"((?<=\n) )|\S", "x", m.group(2))
29 29 return m.group(1) + t + "*/"
30 30
31 31 def repcallspaces(m):
32 32 t = re.sub(r"\n\s+", "\n", m.group(2))
33 33 return m.group(1) + t
34 34
35 35 def repinclude(m):
36 36 return m.group(1) + "<foo>"
37 37
38 38 def rephere(m):
39 39 t = re.sub(r"\S", "x", m.group(2))
40 40 return m.group(1) + t
41 41
42 42
43 43 testpats = [
44 44 (r'(pushd|popd)', "don't use 'pushd' or 'popd', use 'cd'"),
45 45 (r'\W\$?\(\([^\)]*\)\)', "don't use (()) or $(()), use 'expr'"),
46 46 (r'^function', "don't use 'function', use old style"),
47 47 (r'grep.*-q', "don't use 'grep -q', redirect to /dev/null"),
48 48 (r'echo.*\\n', "don't use 'echo \\n', use printf"),
49 49 (r'^diff.*-\w*N', "don't use 'diff -N'"),
50 50 (r'(^| )wc[^|]*$', "filter wc output"),
51 51 (r'head -c', "don't use 'head -c', use 'dd'"),
52 52 (r'ls.*-\w*R', "don't use 'ls -R', use 'find'"),
53 53 (r'printf.*\\\d\d\d', "don't use 'printf \NNN', use Python"),
54 54 (r'printf.*\\x', "don't use printf \\x, use Python"),
55 55 (r'\$\(.*\)', "don't use $(expr), use `expr`"),
56 56 (r'rm -rf \*', "don't use naked rm -rf, target a directory"),
57 57 (r'(^|\|\s*)grep (-\w\s+)*[^|]*[(|]\w',
58 58 "use egrep for extended grep syntax"),
59 59 (r'/bin/', "don't use explicit paths for tools"),
60 60 (r'\$PWD', "don't use $PWD, use `pwd`"),
61 61 (r'[^\n]\Z', "no trailing newline"),
62 62 (r'export.*=', "don't export and assign at once"),
63 63 ('^([^"\']|("[^"]*")|(\'[^\']*\'))*\\^', "^ must be quoted"),
64 64 (r'^source\b', "don't use 'source', use '.'"),
65 65 ]
66 66
67 67 testfilters = [
68 68 (r"( *)(#([^\n]*\S)?)", repcomment),
69 69 (r"<<(\S+)((.|\n)*?\n\1)", rephere),
70 70 ]
71 71
72 72 pypats = [
73 73 (r'^\s*def\s*\w+\s*\(.*,\s*\(',
74 74 "tuple parameter unpacking not available in Python 3+"),
75 75 (r'lambda\s*\(.*,.*\)',
76 76 "tuple parameter unpacking not available in Python 3+"),
77 77 (r'\breduce\s*\(.*', "reduce is not available in Python 3+"),
78 78 (r'^\s*\t', "don't use tabs"),
79 79 (r'\S;\s*\n', "semicolon"),
80 80 (r'\w,\w', "missing whitespace after ,"),
81 81 (r'\w[+/*\-<>]\w', "missing whitespace in expression"),
82 82 (r'^\s+\w+=\w+[^,)]$', "missing whitespace in assignment"),
83 83 (r'.{85}', "line too long"),
84 84 (r'[^\n]\Z', "no trailing newline"),
85 85 # (r'^\s+[^_ ][^_. ]+_[^_]+\s*=', "don't use underbars in identifiers"),
86 86 # (r'\w*[a-z][A-Z]\w*\s*=', "don't use camelcase in identifiers"),
87 87 (r'^\s*(if|while|def|class|except|try)\s[^[]*:\s*[^\]#\s]+',
88 88 "linebreak after :"),
89 89 (r'class\s[^(]:', "old-style class, use class foo(object)"),
90 90 (r'^\s+del\(', "del isn't a function"),
91 91 (r'^\s+except\(', "except isn't a function"),
92 92 (r',]', "unneeded trailing ',' in list"),
93 93 # (r'class\s[A-Z][^\(]*\((?!Exception)',
94 94 # "don't capitalize non-exception classes"),
95 95 # (r'in range\(', "use xrange"),
96 96 # (r'^\s*print\s+', "avoid using print in core and extensions"),
97 97 (r'[\x80-\xff]', "non-ASCII character literal"),
98 98 (r'("\')\.format\(', "str.format() not available in Python 2.4"),
99 99 (r'^\s*with\s+', "with not available in Python 2.4"),
100 100 (r'(?<!def)\s+(any|all|format)\(',
101 101 "any/all/format not available in Python 2.4"),
102 102 (r'(?<!def)\s+(callable)\(',
103 103 "callable not available in Python 3, use hasattr(f, '__call__')"),
104 104 (r'if\s.*\selse', "if ... else form not available in Python 2.4"),
105 105 (r'([\(\[]\s\S)|(\S\s[\)\]])', "gratuitous whitespace in () or []"),
106 106 # (r'\s\s=', "gratuitous whitespace before ="),
107 107 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
108 108 "missing whitespace around operator"),
109 109 (r'[^>< ](\+=|-=|!=|<>|<=|>=|<<=|>>=)\s',
110 110 "missing whitespace around operator"),
111 111 (r'\s(\+=|-=|!=|<>|<=|>=|<<=|>>=)\S',
112 112 "missing whitespace around operator"),
113 113 (r'[^+=*!<>&| -](\s=|=\s)[^= ]',
114 114 "wrong whitespace around ="),
115 115 (r'raise Exception', "don't raise generic exceptions"),
116 (r'ui\.(status|progress|write|note)\([\'\"]x',
116 (r'ui\.(status|progress|write|note|warn)\([\'\"]x',
117 117 "warning: unwrapped ui message"),
118 118 ]
119 119
120 120 pyfilters = [
121 121 (r"""(?msx)(?P<comment>\#.*?$)|
122 122 ((?P<quote>('''|\"\"\"|(?<!')'(?!')|(?<!")"(?!")))
123 123 (?P<text>(([^\\]|\\.)*?))
124 124 (?P=quote))""", reppython),
125 125 ]
126 126
127 127 cpats = [
128 128 (r'//', "don't use //-style comments"),
129 129 (r'^ ', "don't use spaces to indent"),
130 130 (r'\S\t', "don't use tabs except for indent"),
131 131 (r'(\S\s+|^\s+)\n', "trailing whitespace"),
132 132 (r'.{85}', "line too long"),
133 133 (r'(while|if|do|for)\(', "use space after while/if/do/for"),
134 134 (r'return\(', "return is not a function"),
135 135 (r' ;', "no space before ;"),
136 136 (r'\w+\* \w+', "use int *foo, not int* foo"),
137 137 (r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
138 138 (r'\S+ (\+\+|--)', "use foo++, not foo ++"),
139 139 (r'\w,\w', "missing whitespace after ,"),
140 140 (r'\w[+/*]\w', "missing whitespace in expression"),
141 141 (r'^#\s+\w', "use #foo, not # foo"),
142 142 (r'[^\n]\Z', "no trailing newline"),
143 143 ]
144 144
145 145 cfilters = [
146 146 (r'(/\*)(((\*(?!/))|[^*])*)\*/', repccomment),
147 147 (r'''(?P<quote>(?<!")")(?P<text>([^"]|\\")+)"(?!")''', repquote),
148 148 (r'''(#\s*include\s+<)([^>]+)>''', repinclude),
149 149 (r'(\()([^)]+\))', repcallspaces),
150 150 ]
151 151
152 152 checks = [
153 153 ('python', r'.*\.(py|cgi)$', pyfilters, pypats),
154 154 ('test script', r'(.*/)?test-[^.~]*$', testfilters, testpats),
155 155 ('c', r'.*\.c$', cfilters, cpats),
156 156 ]
157 157
158 158 class norepeatlogger(object):
159 159 def __init__(self):
160 160 self._lastseen = None
161 161
162 162 def log(self, fname, lineno, line, msg):
163 163 """print error related a to given line of a given file.
164 164
165 165 The faulty line will also be printed but only once in the case
166 166 of multiple errors.
167 167
168 168 :fname: filename
169 169 :lineno: line number
170 170 :line: actual content of the line
171 171 :msg: error message
172 172 """
173 173 msgid = fname, lineno, line
174 174 if msgid != self._lastseen:
175 175 print "%s:%d:" % (fname, lineno)
176 176 print " > %s" % line
177 177 self._lastseen = msgid
178 178 print " " + msg
179 179
180 180 _defaultlogger = norepeatlogger()
181 181
182 182 def checkfile(f, logfunc=_defaultlogger.log, maxerr=None, warnings=False):
183 183 """checks style and portability of a given file
184 184
185 185 :f: filepath
186 186 :logfunc: function used to report error
187 187 logfunc(filename, linenumber, linecontent, errormessage)
188 188 :maxerr: number of error to display before arborting.
189 189 Set to None (default) to report all errors
190 190
191 191 return True if no error is found, False otherwise.
192 192 """
193 193 result = True
194 194 for name, match, filters, pats in checks:
195 195 fc = 0
196 196 if not re.match(match, f):
197 197 continue
198 198 pre = post = open(f).read()
199 199 if "no-" + "check-code" in pre:
200 200 break
201 201 for p, r in filters:
202 202 post = re.sub(p, r, post)
203 203 # print post # uncomment to show filtered version
204 204 z = enumerate(zip(pre.splitlines(), post.splitlines(True)))
205 205 for n, l in z:
206 206 if "check-code" + "-ignore" in l[0]:
207 207 continue
208 208 for p, msg in pats:
209 209 if not warnings and msg.startswith("warning"):
210 210 continue
211 211 if re.search(p, l[1]):
212 212 logfunc(f, n + 1, l[0], msg)
213 213 fc += 1
214 214 result = False
215 215 if maxerr is not None and fc >= maxerr:
216 216 print " (too many errors, giving up)"
217 217 break
218 218 break
219 219 return result
220 220
221 221
222 222 if __name__ == "__main__":
223 223 parser = optparse.OptionParser("%prog [options] [files]")
224 224 parser.add_option("-w", "--warnings", action="store_true",
225 225 help="include warning-level checks")
226 226 parser.add_option("-p", "--per-file", type="int",
227 227 help="max warnings per file")
228 228
229 229 parser.set_defaults(per_file=15, warnings=False)
230 230 (options, args) = parser.parse_args()
231 231
232 232 if len(args) == 0:
233 233 check = glob.glob("*")
234 234 else:
235 235 check = args
236 236
237 237 for f in check:
238 238 checkfile(f, maxerr=options.per_file, warnings=options.warnings)
@@ -1,550 +1,550 b''
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 or any later version.
7 7
8 8 from i18n import _
9 9 import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback
10 10 import util, commands, hg, fancyopts, extensions, hook, error
11 11 import cmdutil, encoding
12 12 import ui as uimod
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 = uimod.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 if inst.hint:
27 27 sys.stdout.write(_("(%s)\n") % inst.hint)
28 28 return -1
29 29 except error.ParseError, inst:
30 30 if len(inst.args) > 1:
31 31 sys.stderr.write(_("hg: parse error at %s: %s\n") %
32 32 (inst.args[1], inst.args[0]))
33 33 else:
34 34 sys.stderr.write(_("hg: parse error: %s\n") % inst.args[0])
35 35 return -1
36 36 return _runcatch(u, args)
37 37
38 38 def _runcatch(ui, args):
39 39 def catchterm(*args):
40 40 raise error.SignalInterrupt
41 41
42 42 try:
43 43 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
44 44 num = getattr(signal, name, None)
45 45 if num:
46 46 signal.signal(num, catchterm)
47 47 except ValueError:
48 48 pass # happens if called in a thread
49 49
50 50 try:
51 51 try:
52 52 # enter the debugger before command execution
53 53 if '--debugger' in args:
54 54 ui.warn(_("entering debugger - "
55 55 "type c to continue starting hg or h for help\n"))
56 56 pdb.set_trace()
57 57 try:
58 58 return _dispatch(ui, args)
59 59 finally:
60 60 ui.flush()
61 61 except:
62 62 # enter the debugger when we hit an exception
63 63 if '--debugger' in args:
64 64 traceback.print_exc()
65 65 pdb.post_mortem(sys.exc_info()[2])
66 66 ui.traceback()
67 67 raise
68 68
69 69 # Global exception handling, alphabetically
70 70 # Mercurial-specific first, followed by built-in and library exceptions
71 71 except error.AmbiguousCommand, inst:
72 72 ui.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
73 73 (inst.args[0], " ".join(inst.args[1])))
74 74 except error.ParseError, inst:
75 75 if len(inst.args) > 1:
76 76 ui.warn(_("hg: parse error at %s: %s\n") %
77 77 (inst.args[1], inst.args[0]))
78 78 else:
79 79 ui.warn(_("hg: parse error: %s\n") % inst.args[0])
80 80 return -1
81 81 except error.LockHeld, inst:
82 82 if inst.errno == errno.ETIMEDOUT:
83 83 reason = _('timed out waiting for lock held by %s') % inst.locker
84 84 else:
85 85 reason = _('lock held by %s') % inst.locker
86 86 ui.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
87 87 except error.LockUnavailable, inst:
88 88 ui.warn(_("abort: could not lock %s: %s\n") %
89 89 (inst.desc or inst.filename, inst.strerror))
90 90 except error.CommandError, inst:
91 91 if inst.args[0]:
92 92 ui.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
93 93 commands.help_(ui, inst.args[0])
94 94 else:
95 95 ui.warn(_("hg: %s\n") % inst.args[1])
96 96 commands.help_(ui, 'shortlist')
97 97 except error.RepoError, inst:
98 98 ui.warn(_("abort: %s!\n") % inst)
99 99 except error.ResponseError, inst:
100 100 ui.warn(_("abort: %s") % inst.args[0])
101 101 if not isinstance(inst.args[1], basestring):
102 102 ui.warn(" %r\n" % (inst.args[1],))
103 103 elif not inst.args[1]:
104 104 ui.warn(_(" empty string\n"))
105 105 else:
106 106 ui.warn("\n%r\n" % util.ellipsis(inst.args[1]))
107 107 except error.RevlogError, inst:
108 108 ui.warn(_("abort: %s!\n") % inst)
109 109 except error.SignalInterrupt:
110 110 ui.warn(_("killed!\n"))
111 111 except error.UnknownCommand, inst:
112 112 ui.warn(_("hg: unknown command '%s'\n") % inst.args[0])
113 113 try:
114 114 # check if the command is in a disabled extension
115 115 # (but don't check for extensions themselves)
116 116 commands.help_(ui, inst.args[0], unknowncmd=True)
117 117 except error.UnknownCommand:
118 118 commands.help_(ui, 'shortlist')
119 119 except util.Abort, inst:
120 120 ui.warn(_("abort: %s\n") % inst)
121 121 if inst.hint:
122 122 ui.status(_("(%s)\n") % inst.hint)
123 123 except ImportError, inst:
124 124 ui.warn(_("abort: %s!\n") % inst)
125 125 m = str(inst).split()[-1]
126 126 if m in "mpatch bdiff".split():
127 127 ui.warn(_("(did you forget to compile extensions?)\n"))
128 128 elif m in "zlib".split():
129 129 ui.warn(_("(is your Python install correct?)\n"))
130 130 except IOError, inst:
131 131 if hasattr(inst, "code"):
132 132 ui.warn(_("abort: %s\n") % inst)
133 133 elif hasattr(inst, "reason"):
134 134 try: # usually it is in the form (errno, strerror)
135 135 reason = inst.reason.args[1]
136 136 except: # it might be anything, for example a string
137 137 reason = inst.reason
138 138 ui.warn(_("abort: error: %s\n") % reason)
139 139 elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
140 140 if ui.debugflag:
141 141 ui.warn(_("broken pipe\n"))
142 142 elif getattr(inst, "strerror", None):
143 143 if getattr(inst, "filename", None):
144 144 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
145 145 else:
146 146 ui.warn(_("abort: %s\n") % inst.strerror)
147 147 else:
148 148 raise
149 149 except OSError, inst:
150 150 if getattr(inst, "filename", None):
151 151 ui.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
152 152 else:
153 153 ui.warn(_("abort: %s\n") % inst.strerror)
154 154 except KeyboardInterrupt:
155 155 try:
156 156 ui.warn(_("interrupted!\n"))
157 157 except IOError, inst:
158 158 if inst.errno == errno.EPIPE:
159 159 if ui.debugflag:
160 160 ui.warn(_("\nbroken pipe\n"))
161 161 else:
162 162 raise
163 163 except MemoryError:
164 164 ui.warn(_("abort: out of memory\n"))
165 165 except SystemExit, inst:
166 166 # Commands shouldn't sys.exit directly, but give a return code.
167 167 # Just in case catch this and and pass exit code to caller.
168 168 return inst.code
169 169 except socket.error, inst:
170 170 ui.warn(_("abort: %s\n") % inst.args[-1])
171 171 except:
172 172 ui.warn(_("** unknown exception encountered, details follow\n"))
173 173 ui.warn(_("** report bug details to "
174 174 "http://mercurial.selenic.com/bts/\n"))
175 175 ui.warn(_("** or mercurial@selenic.com\n"))
176 176 ui.warn(_("** Python %s\n") % sys.version.replace('\n', ''))
177 177 ui.warn(_("** Mercurial Distributed SCM (version %s)\n")
178 178 % util.version())
179 179 ui.warn(_("** Extensions loaded: %s\n")
180 180 % ", ".join([x[0] for x in extensions.extensions()]))
181 181 raise
182 182
183 183 return -1
184 184
185 185 def aliasargs(fn):
186 186 if hasattr(fn, 'args'):
187 187 return fn.args
188 188 return []
189 189
190 190 class cmdalias(object):
191 191 def __init__(self, name, definition, cmdtable):
192 192 self.name = name
193 193 self.definition = definition
194 194 self.args = []
195 195 self.opts = []
196 196 self.help = ''
197 197 self.norepo = True
198 198 self.badalias = False
199 199
200 200 try:
201 201 cmdutil.findcmd(self.name, cmdtable, True)
202 202 self.shadows = True
203 203 except error.UnknownCommand:
204 204 self.shadows = False
205 205
206 206 if not self.definition:
207 207 def fn(ui, *args):
208 208 ui.warn(_("no definition for alias '%s'\n") % self.name)
209 209 return 1
210 210 self.fn = fn
211 211 self.badalias = True
212 212
213 213 return
214 214
215 215 if self.definition.startswith('!'):
216 216 def fn(ui, *args):
217 217 cmd = '%s %s' % (self.definition[1:], ' '.join(args))
218 218 return util.system(cmd)
219 219 self.fn = fn
220 220 return
221 221
222 222 args = shlex.split(self.definition)
223 223 cmd = args.pop(0)
224 224 args = map(util.expandpath, args)
225 225
226 226 try:
227 227 tableentry = cmdutil.findcmd(cmd, cmdtable, False)[1]
228 228 if len(tableentry) > 2:
229 229 self.fn, self.opts, self.help = tableentry
230 230 else:
231 231 self.fn, self.opts = tableentry
232 232
233 233 self.args = aliasargs(self.fn) + args
234 234 if cmd not in commands.norepo.split(' '):
235 235 self.norepo = False
236 236 if self.help.startswith("hg " + cmd):
237 237 # drop prefix in old-style help lines so hg shows the alias
238 238 self.help = self.help[4 + len(cmd):]
239 239 self.__doc__ = self.fn.__doc__
240 240
241 241 except error.UnknownCommand:
242 242 def fn(ui, *args):
243 243 ui.warn(_("alias '%s' resolves to unknown command '%s'\n") \
244 244 % (self.name, cmd))
245 245 try:
246 246 # check if the command is in a disabled extension
247 247 commands.help_(ui, cmd, unknowncmd=True)
248 248 except error.UnknownCommand:
249 249 pass
250 250 return 1
251 251 self.fn = fn
252 252 self.badalias = True
253 253 except error.AmbiguousCommand:
254 254 def fn(ui, *args):
255 255 ui.warn(_("alias '%s' resolves to ambiguous command '%s'\n") \
256 256 % (self.name, cmd))
257 257 return 1
258 258 self.fn = fn
259 259 self.badalias = True
260 260
261 261 def __call__(self, ui, *args, **opts):
262 262 if self.shadows:
263 263 ui.debug("alias '%s' shadows command\n" % self.name)
264 264
265 265 return self.fn(ui, *args, **opts)
266 266
267 267 def addaliases(ui, cmdtable):
268 268 # aliases are processed after extensions have been loaded, so they
269 269 # may use extension commands. Aliases can also use other alias definitions,
270 270 # but only if they have been defined prior to the current definition.
271 271 for alias, definition in ui.configitems('alias'):
272 272 aliasdef = cmdalias(alias, definition, cmdtable)
273 273 cmdtable[alias] = (aliasdef, aliasdef.opts, aliasdef.help)
274 274 if aliasdef.norepo:
275 275 commands.norepo += ' %s' % alias
276 276
277 277 def _parse(ui, args):
278 278 options = {}
279 279 cmdoptions = {}
280 280
281 281 try:
282 282 args = fancyopts.fancyopts(args, commands.globalopts, options)
283 283 except fancyopts.getopt.GetoptError, inst:
284 284 raise error.CommandError(None, inst)
285 285
286 286 if args:
287 287 cmd, args = args[0], args[1:]
288 288 aliases, entry = cmdutil.findcmd(cmd, commands.table,
289 289 ui.config("ui", "strict"))
290 290 cmd = aliases[0]
291 291 args = aliasargs(entry[0]) + args
292 292 defaults = ui.config("defaults", cmd)
293 293 if defaults:
294 294 args = map(util.expandpath, shlex.split(defaults)) + args
295 295 c = list(entry[1])
296 296 else:
297 297 cmd = None
298 298 c = []
299 299
300 300 # combine global options into local
301 301 for o in commands.globalopts:
302 302 c.append((o[0], o[1], options[o[1]], o[3]))
303 303
304 304 try:
305 305 args = fancyopts.fancyopts(args, c, cmdoptions, True)
306 306 except fancyopts.getopt.GetoptError, inst:
307 307 raise error.CommandError(cmd, inst)
308 308
309 309 # separate global options back out
310 310 for o in commands.globalopts:
311 311 n = o[1]
312 312 options[n] = cmdoptions[n]
313 313 del cmdoptions[n]
314 314
315 315 return (cmd, cmd and entry[0] or None, args, options, cmdoptions)
316 316
317 317 def _parseconfig(ui, config):
318 318 """parse the --config options from the command line"""
319 319 for cfg in config:
320 320 try:
321 321 name, value = cfg.split('=', 1)
322 322 section, name = name.split('.', 1)
323 323 if not section or not name:
324 324 raise IndexError
325 325 ui.setconfig(section, name, value)
326 326 except (IndexError, ValueError):
327 327 raise util.Abort(_('malformed --config option: %r '
328 328 '(use --config section.name=value)') % cfg)
329 329
330 330 def _earlygetopt(aliases, args):
331 331 """Return list of values for an option (or aliases).
332 332
333 333 The values are listed in the order they appear in args.
334 334 The options and values are removed from args.
335 335 """
336 336 try:
337 337 argcount = args.index("--")
338 338 except ValueError:
339 339 argcount = len(args)
340 340 shortopts = [opt for opt in aliases if len(opt) == 2]
341 341 values = []
342 342 pos = 0
343 343 while pos < argcount:
344 344 if args[pos] in aliases:
345 345 if pos + 1 >= argcount:
346 346 # ignore and let getopt report an error if there is no value
347 347 break
348 348 del args[pos]
349 349 values.append(args.pop(pos))
350 350 argcount -= 2
351 351 elif args[pos][:2] in shortopts:
352 352 # short option can have no following space, e.g. hg log -Rfoo
353 353 values.append(args.pop(pos)[2:])
354 354 argcount -= 1
355 355 else:
356 356 pos += 1
357 357 return values
358 358
359 359 def runcommand(lui, repo, cmd, fullargs, ui, options, d, cmdpats, cmdoptions):
360 360 # run pre-hook, and abort if it fails
361 361 ret = hook.hook(lui, repo, "pre-%s" % cmd, False, args=" ".join(fullargs),
362 362 pats=cmdpats, opts=cmdoptions)
363 363 if ret:
364 364 return ret
365 365 ret = _runcommand(ui, options, cmd, d)
366 366 # run post-hook, passing command result
367 367 hook.hook(lui, repo, "post-%s" % cmd, False, args=" ".join(fullargs),
368 368 result=ret, pats=cmdpats, opts=cmdoptions)
369 369 return ret
370 370
371 371 _loaded = set()
372 372 def _dispatch(ui, args):
373 373 # read --config before doing anything else
374 374 # (e.g. to change trust settings for reading .hg/hgrc)
375 375 _parseconfig(ui, _earlygetopt(['--config'], args))
376 376
377 377 # check for cwd
378 378 cwd = _earlygetopt(['--cwd'], args)
379 379 if cwd:
380 380 os.chdir(cwd[-1])
381 381
382 382 # read the local repository .hgrc into a local ui object
383 383 path = cmdutil.findrepo(os.getcwd()) or ""
384 384 if not path:
385 385 lui = ui
386 386 else:
387 387 try:
388 388 lui = ui.copy()
389 389 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
390 390 except IOError:
391 391 pass
392 392
393 393 # now we can expand paths, even ones in .hg/hgrc
394 394 rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
395 395 if rpath:
396 396 path = lui.expandpath(rpath[-1])
397 397 lui = ui.copy()
398 398 lui.readconfig(os.path.join(path, ".hg", "hgrc"))
399 399
400 400 # Configure extensions in phases: uisetup, extsetup, cmdtable, and
401 401 # reposetup. Programs like TortoiseHg will call _dispatch several
402 402 # times so we keep track of configured extensions in _loaded.
403 403 extensions.loadall(lui)
404 404 exts = [ext for ext in extensions.extensions() if ext[0] not in _loaded]
405 405 # Propagate any changes to lui.__class__ by extensions
406 406 ui.__class__ = lui.__class__
407 407
408 408 # (uisetup and extsetup are handled in extensions.loadall)
409 409
410 410 for name, module in exts:
411 411 cmdtable = getattr(module, 'cmdtable', {})
412 412 overrides = [cmd for cmd in cmdtable if cmd in commands.table]
413 413 if overrides:
414 414 ui.warn(_("extension '%s' overrides commands: %s\n")
415 415 % (name, " ".join(overrides)))
416 416 commands.table.update(cmdtable)
417 417 _loaded.add(name)
418 418
419 419 # (reposetup is handled in hg.repository)
420 420
421 421 addaliases(lui, commands.table)
422 422
423 423 # check for fallback encoding
424 424 fallback = lui.config('ui', 'fallbackencoding')
425 425 if fallback:
426 426 encoding.fallbackencoding = fallback
427 427
428 428 fullargs = args
429 429 cmd, func, args, options, cmdoptions = _parse(lui, args)
430 430
431 431 if options["config"]:
432 432 raise util.Abort(_("Option --config may not be abbreviated!"))
433 433 if options["cwd"]:
434 434 raise util.Abort(_("Option --cwd may not be abbreviated!"))
435 435 if options["repository"]:
436 436 raise util.Abort(_(
437 437 "Option -R has to be separated from other options (e.g. not -qR) "
438 438 "and --repository may only be abbreviated as --repo!"))
439 439
440 440 if options["encoding"]:
441 441 encoding.encoding = options["encoding"]
442 442 if options["encodingmode"]:
443 443 encoding.encodingmode = options["encodingmode"]
444 444 if options["time"]:
445 445 def get_times():
446 446 t = os.times()
447 447 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
448 448 t = (t[0], t[1], t[2], t[3], time.clock())
449 449 return t
450 450 s = get_times()
451 451 def print_time():
452 452 t = get_times()
453 453 ui.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
454 454 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
455 455 atexit.register(print_time)
456 456
457 457 if options['verbose'] or options['debug'] or options['quiet']:
458 458 ui.setconfig('ui', 'verbose', str(bool(options['verbose'])))
459 459 ui.setconfig('ui', 'debug', str(bool(options['debug'])))
460 460 ui.setconfig('ui', 'quiet', str(bool(options['quiet'])))
461 461 if options['traceback']:
462 462 ui.setconfig('ui', 'traceback', 'on')
463 463 if options['noninteractive']:
464 464 ui.setconfig('ui', 'interactive', 'off')
465 465
466 466 if options['help']:
467 467 return commands.help_(ui, cmd, options['version'])
468 468 elif options['version']:
469 469 return commands.version_(ui)
470 470 elif not cmd:
471 471 return commands.help_(ui, 'shortlist')
472 472
473 473 repo = None
474 474 cmdpats = args[:]
475 475 if cmd not in commands.norepo.split():
476 476 try:
477 477 repo = hg.repository(ui, path=path)
478 478 ui = repo.ui
479 479 if not repo.local():
480 480 raise util.Abort(_("repository '%s' is not local") % path)
481 481 ui.setconfig("bundle", "mainreporoot", repo.root)
482 482 except error.RepoError:
483 483 if cmd not in commands.optionalrepo.split():
484 484 if args and not path: # try to infer -R from command args
485 485 repos = map(cmdutil.findrepo, args)
486 486 guess = repos[0]
487 487 if guess and repos.count(guess) == len(repos):
488 488 return _dispatch(ui, ['--repository', guess] + fullargs)
489 489 if not path:
490 490 raise error.RepoError(_("There is no Mercurial repository"
491 491 " here (.hg not found)"))
492 492 raise
493 493 args.insert(0, repo)
494 494 elif rpath:
495 ui.warn("warning: --repository ignored\n")
495 ui.warn(_("warning: --repository ignored\n"))
496 496
497 497 d = lambda: util.checksignature(func)(ui, *args, **cmdoptions)
498 498 return runcommand(lui, repo, cmd, fullargs, ui, options, d,
499 499 cmdpats, cmdoptions)
500 500
501 501 def _runcommand(ui, options, cmd, cmdfunc):
502 502 def checkargs():
503 503 try:
504 504 return cmdfunc()
505 505 except error.SignatureError:
506 506 raise error.CommandError(cmd, _("invalid arguments"))
507 507
508 508 if options['profile']:
509 509 format = ui.config('profiling', 'format', default='text')
510 510
511 511 if not format in ['text', 'kcachegrind']:
512 512 ui.warn(_("unrecognized profiling format '%s'"
513 513 " - Ignored\n") % format)
514 514 format = 'text'
515 515
516 516 output = ui.config('profiling', 'output')
517 517
518 518 if output:
519 519 path = ui.expandpath(output)
520 520 ostream = open(path, 'wb')
521 521 else:
522 522 ostream = sys.stderr
523 523
524 524 try:
525 525 from mercurial import lsprof
526 526 except ImportError:
527 527 raise util.Abort(_(
528 528 'lsprof not available - install from '
529 529 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
530 530 p = lsprof.Profiler()
531 531 p.enable(subcalls=True)
532 532 try:
533 533 return checkargs()
534 534 finally:
535 535 p.disable()
536 536
537 537 if format == 'kcachegrind':
538 538 import lsprofcalltree
539 539 calltree = lsprofcalltree.KCacheGrind(p)
540 540 calltree.output(ostream)
541 541 else:
542 542 # format == 'text'
543 543 stats = lsprof.Stats(p.getstats())
544 544 stats.sort()
545 545 stats.pprint(top=10, file=ostream, climit=5)
546 546
547 547 if output:
548 548 ostream.close()
549 549 else:
550 550 return checkargs()
@@ -1,159 +1,160 b''
1 1 # repair.py - functions for repository repair for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 4 # Copyright 2007 Matt Mackall
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9 import changegroup
10 10 from node import nullrev, short
11 11 from i18n import _
12 12 import os
13 13
14 14 def _bundle(repo, bases, heads, node, suffix, extranodes=None):
15 15 """create a bundle with the specified revisions as a backup"""
16 16 cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
17 17 backupdir = repo.join("strip-backup")
18 18 if not os.path.isdir(backupdir):
19 19 os.mkdir(backupdir)
20 20 name = os.path.join(backupdir, "%s-%s.hg" % (short(node), suffix))
21 21 return changegroup.writebundle(cg, name, "HG10BZ")
22 22
23 23 def _collectfiles(repo, striprev):
24 24 """find out the filelogs affected by the strip"""
25 25 files = set()
26 26
27 27 for x in xrange(striprev, len(repo)):
28 28 files.update(repo[x].files())
29 29
30 30 return sorted(files)
31 31
32 32 def _collectextranodes(repo, files, link):
33 33 """return the nodes that have to be saved before the strip"""
34 34 def collectone(revlog):
35 35 extra = []
36 36 startrev = count = len(revlog)
37 37 # find the truncation point of the revlog
38 38 for i in xrange(count):
39 39 lrev = revlog.linkrev(i)
40 40 if lrev >= link:
41 41 startrev = i + 1
42 42 break
43 43
44 44 # see if any revision after that point has a linkrev less than link
45 45 # (we have to manually save these guys)
46 46 for i in xrange(startrev, count):
47 47 node = revlog.node(i)
48 48 lrev = revlog.linkrev(i)
49 49 if lrev < link:
50 50 extra.append((node, cl.node(lrev)))
51 51
52 52 return extra
53 53
54 54 extranodes = {}
55 55 cl = repo.changelog
56 56 extra = collectone(repo.manifest)
57 57 if extra:
58 58 extranodes[1] = extra
59 59 for fname in files:
60 60 f = repo.file(fname)
61 61 extra = collectone(f)
62 62 if extra:
63 63 extranodes[fname] = extra
64 64
65 65 return extranodes
66 66
67 67 def strip(ui, repo, node, backup="all"):
68 68 cl = repo.changelog
69 69 # TODO delete the undo files, and handle undo of merge sets
70 70 striprev = cl.rev(node)
71 71
72 72 # Some revisions with rev > striprev may not be descendants of striprev.
73 73 # We have to find these revisions and put them in a bundle, so that
74 74 # we can restore them after the truncations.
75 75 # To create the bundle we use repo.changegroupsubset which requires
76 76 # the list of heads and bases of the set of interesting revisions.
77 77 # (head = revision in the set that has no descendant in the set;
78 78 # base = revision in the set that has no ancestor in the set)
79 79 tostrip = set((striprev,))
80 80 saveheads = set()
81 81 savebases = []
82 82 for r in xrange(striprev + 1, len(cl)):
83 83 parents = cl.parentrevs(r)
84 84 if parents[0] in tostrip or parents[1] in tostrip:
85 85 # r is a descendant of striprev
86 86 tostrip.add(r)
87 87 # if this is a merge and one of the parents does not descend
88 88 # from striprev, mark that parent as a savehead.
89 89 if parents[1] != nullrev:
90 90 for p in parents:
91 91 if p not in tostrip and p > striprev:
92 92 saveheads.add(p)
93 93 else:
94 94 # if no parents of this revision will be stripped, mark it as
95 95 # a savebase
96 96 if parents[0] < striprev and parents[1] < striprev:
97 97 savebases.append(cl.node(r))
98 98
99 99 saveheads.difference_update(parents)
100 100 saveheads.add(r)
101 101
102 102 saveheads = [cl.node(r) for r in saveheads]
103 103 files = _collectfiles(repo, striprev)
104 104
105 105 extranodes = _collectextranodes(repo, files, striprev)
106 106
107 107 # create a changegroup for all the branches we need to keep
108 108 backupfile = None
109 109 if backup == "all":
110 110 backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
111 111 repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
112 112 if saveheads or extranodes:
113 113 chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
114 114 extranodes)
115 115
116 116 mfst = repo.manifest
117 117
118 118 tr = repo.transaction("strip")
119 119 offset = len(tr.entries)
120 120
121 121 try:
122 122 tr.startgroup()
123 123 cl.strip(striprev, tr)
124 124 mfst.strip(striprev, tr)
125 125 for fn in files:
126 126 repo.file(fn).strip(striprev, tr)
127 127 tr.endgroup()
128 128
129 129 try:
130 130 for i in xrange(offset, len(tr.entries)):
131 131 file, troffset, ignore = tr.entries[i]
132 132 repo.sopener(file, 'a').truncate(troffset)
133 133 tr.close()
134 134 except:
135 135 tr.abort()
136 136 raise
137 137
138 138 if saveheads or extranodes:
139 139 ui.note(_("adding branch\n"))
140 140 f = open(chgrpfile, "rb")
141 141 gen = changegroup.readbundle(f, chgrpfile)
142 142 if not repo.ui.verbose:
143 143 # silence internal shuffling chatter
144 144 repo.ui.pushbuffer()
145 145 repo.addchangegroup(gen, 'strip', 'bundle:' + chgrpfile, True)
146 146 if not repo.ui.verbose:
147 147 repo.ui.popbuffer()
148 148 f.close()
149 149 if backup != "strip":
150 150 os.unlink(chgrpfile)
151 151 except:
152 152 if backupfile:
153 ui.warn("strip failed, full bundle stored in '%s'\n" % backupfile)
153 ui.warn(_("strip failed, full bundle stored in '%s'\n")
154 % backupfile)
154 155 elif saveheads:
155 ui.warn("strip failed, partial bundle stored in '%s'\n"
156 ui.warn(_("strip failed, partial bundle stored in '%s'\n")
156 157 % chgrpfile)
157 158 raise
158 159
159 160 repo.destroyed()
@@ -1,604 +1,604 b''
1 1 # ui.py - user interface bits 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 or any later version.
7 7
8 8 from i18n import _
9 9 import errno, getpass, os, socket, sys, tempfile, traceback
10 10 import config, util, error
11 11
12 12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 13 '0': False, 'no': False, 'false': False, 'off': False}
14 14
15 15 class ui(object):
16 16 def __init__(self, src=None):
17 17 self._buffers = []
18 18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 19 self._reportuntrusted = True
20 20 self._ocfg = config.config() # overlay
21 21 self._tcfg = config.config() # trusted
22 22 self._ucfg = config.config() # untrusted
23 23 self._trustusers = set()
24 24 self._trustgroups = set()
25 25
26 26 if src:
27 27 self._tcfg = src._tcfg.copy()
28 28 self._ucfg = src._ucfg.copy()
29 29 self._ocfg = src._ocfg.copy()
30 30 self._trustusers = src._trustusers.copy()
31 31 self._trustgroups = src._trustgroups.copy()
32 32 self.environ = src.environ
33 33 self.fixconfig()
34 34 else:
35 35 # shared read-only environment
36 36 self.environ = os.environ
37 37 # we always trust global config files
38 38 for f in util.rcpath():
39 39 self.readconfig(f, trust=True)
40 40
41 41 def copy(self):
42 42 return self.__class__(self)
43 43
44 44 def _is_trusted(self, fp, f):
45 45 st = util.fstat(fp)
46 46 if util.isowner(st):
47 47 return True
48 48
49 49 tusers, tgroups = self._trustusers, self._trustgroups
50 50 if '*' in tusers or '*' in tgroups:
51 51 return True
52 52
53 53 user = util.username(st.st_uid)
54 54 group = util.groupname(st.st_gid)
55 55 if user in tusers or group in tgroups or user == util.username():
56 56 return True
57 57
58 58 if self._reportuntrusted:
59 59 self.warn(_('Not trusting file %s from untrusted '
60 60 'user %s, group %s\n') % (f, user, group))
61 61 return False
62 62
63 63 def readconfig(self, filename, root=None, trust=False,
64 64 sections=None, remap=None):
65 65 try:
66 66 fp = open(filename)
67 67 except IOError:
68 68 if not sections: # ignore unless we were looking for something
69 69 return
70 70 raise
71 71
72 72 cfg = config.config()
73 73 trusted = sections or trust or self._is_trusted(fp, filename)
74 74
75 75 try:
76 76 cfg.read(filename, fp, sections=sections, remap=remap)
77 77 except error.ConfigError, inst:
78 78 if trusted:
79 79 raise
80 80 self.warn(_("Ignored: %s\n") % str(inst))
81 81
82 82 if self.plain():
83 83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
84 84 'logtemplate', 'style',
85 85 'traceback', 'verbose'):
86 86 if k in cfg['ui']:
87 87 del cfg['ui'][k]
88 88 for k, v in cfg.items('alias'):
89 89 del cfg['alias'][k]
90 90 for k, v in cfg.items('defaults'):
91 91 del cfg['defaults'][k]
92 92
93 93 if trusted:
94 94 self._tcfg.update(cfg)
95 95 self._tcfg.update(self._ocfg)
96 96 self._ucfg.update(cfg)
97 97 self._ucfg.update(self._ocfg)
98 98
99 99 if root is None:
100 100 root = os.path.expanduser('~')
101 101 self.fixconfig(root=root)
102 102
103 103 def fixconfig(self, root=None):
104 104 # translate paths relative to root (or home) into absolute paths
105 105 root = root or os.getcwd()
106 106 for c in self._tcfg, self._ucfg, self._ocfg:
107 107 for n, p in c.items('paths'):
108 108 if p and "://" not in p and not os.path.isabs(p):
109 109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
110 110
111 111 # update ui options
112 112 self.debugflag = self.configbool('ui', 'debug')
113 113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
114 114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
115 115 if self.verbose and self.quiet:
116 116 self.quiet = self.verbose = False
117 117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
118 118 self.tracebackflag = self.configbool('ui', 'traceback', False)
119 119
120 120 # update trust information
121 121 self._trustusers.update(self.configlist('trusted', 'users'))
122 122 self._trustgroups.update(self.configlist('trusted', 'groups'))
123 123
124 124 def setconfig(self, section, name, value):
125 125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
126 126 cfg.set(section, name, value)
127 127 self.fixconfig()
128 128
129 129 def _data(self, untrusted):
130 130 return untrusted and self._ucfg or self._tcfg
131 131
132 132 def configsource(self, section, name, untrusted=False):
133 133 return self._data(untrusted).source(section, name) or 'none'
134 134
135 135 def config(self, section, name, default=None, untrusted=False):
136 136 value = self._data(untrusted).get(section, name, default)
137 137 if self.debugflag and not untrusted and self._reportuntrusted:
138 138 uvalue = self._ucfg.get(section, name)
139 139 if uvalue is not None and uvalue != value:
140 140 self.debug(_("ignoring untrusted configuration option "
141 141 "%s.%s = %s\n") % (section, name, uvalue))
142 142 return value
143 143
144 144 def configbool(self, section, name, default=False, untrusted=False):
145 145 v = self.config(section, name, None, untrusted)
146 146 if v is None:
147 147 return default
148 148 if isinstance(v, bool):
149 149 return v
150 150 if v.lower() not in _booleans:
151 151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
152 152 % (section, name, v))
153 153 return _booleans[v.lower()]
154 154
155 155 def configlist(self, section, name, default=None, untrusted=False):
156 156 """Return a list of comma/space separated strings"""
157 157
158 158 def _parse_plain(parts, s, offset):
159 159 whitespace = False
160 160 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
161 161 whitespace = True
162 162 offset += 1
163 163 if offset >= len(s):
164 164 return None, parts, offset
165 165 if whitespace:
166 166 parts.append('')
167 167 if s[offset] == '"' and not parts[-1]:
168 168 return _parse_quote, parts, offset + 1
169 169 elif s[offset] == '"' and parts[-1][-1] == '\\':
170 170 parts[-1] = parts[-1][:-1] + s[offset]
171 171 return _parse_plain, parts, offset + 1
172 172 parts[-1] += s[offset]
173 173 return _parse_plain, parts, offset + 1
174 174
175 175 def _parse_quote(parts, s, offset):
176 176 if offset < len(s) and s[offset] == '"': # ""
177 177 parts.append('')
178 178 offset += 1
179 179 while offset < len(s) and (s[offset].isspace() or
180 180 s[offset] == ','):
181 181 offset += 1
182 182 return _parse_plain, parts, offset
183 183
184 184 while offset < len(s) and s[offset] != '"':
185 185 if (s[offset] == '\\' and offset + 1 < len(s)
186 186 and s[offset + 1] == '"'):
187 187 offset += 1
188 188 parts[-1] += '"'
189 189 else:
190 190 parts[-1] += s[offset]
191 191 offset += 1
192 192
193 193 if offset >= len(s):
194 194 real_parts = _configlist(parts[-1])
195 195 if not real_parts:
196 196 parts[-1] = '"'
197 197 else:
198 198 real_parts[0] = '"' + real_parts[0]
199 199 parts = parts[:-1]
200 200 parts.extend(real_parts)
201 201 return None, parts, offset
202 202
203 203 offset += 1
204 204 while offset < len(s) and s[offset] in [' ', ',']:
205 205 offset += 1
206 206
207 207 if offset < len(s):
208 208 if offset + 1 == len(s) and s[offset] == '"':
209 209 parts[-1] += '"'
210 210 offset += 1
211 211 else:
212 212 parts.append('')
213 213 else:
214 214 return None, parts, offset
215 215
216 216 return _parse_plain, parts, offset
217 217
218 218 def _configlist(s):
219 219 s = s.rstrip(' ,')
220 220 if not s:
221 221 return None
222 222 parser, parts, offset = _parse_plain, [''], 0
223 223 while parser:
224 224 parser, parts, offset = parser(parts, s, offset)
225 225 return parts
226 226
227 227 result = self.config(section, name, untrusted=untrusted)
228 228 if result is None:
229 229 result = default or []
230 230 if isinstance(result, basestring):
231 231 result = _configlist(result.lstrip(' ,\n'))
232 232 if result is None:
233 233 result = default or []
234 234 return result
235 235
236 236 def has_section(self, section, untrusted=False):
237 237 '''tell whether section exists in config.'''
238 238 return section in self._data(untrusted)
239 239
240 240 def configitems(self, section, untrusted=False):
241 241 items = self._data(untrusted).items(section)
242 242 if self.debugflag and not untrusted and self._reportuntrusted:
243 243 for k, v in self._ucfg.items(section):
244 244 if self._tcfg.get(section, k) != v:
245 245 self.debug(_("ignoring untrusted configuration option "
246 246 "%s.%s = %s\n") % (section, k, v))
247 247 return items
248 248
249 249 def walkconfig(self, untrusted=False):
250 250 cfg = self._data(untrusted)
251 251 for section in cfg.sections():
252 252 for name, value in self.configitems(section, untrusted):
253 253 yield section, name, str(value).replace('\n', '\\n')
254 254
255 255 def plain(self):
256 256 '''is plain mode active?
257 257
258 258 Plain mode means that all configuration variables which affect the
259 259 behavior and output of Mercurial should be ignored. Additionally, the
260 260 output should be stable, reproducible and suitable for use in scripts or
261 261 applications.
262 262
263 263 The only way to trigger plain mode is by setting the `HGPLAIN'
264 264 environment variable.
265 265 '''
266 266 return 'HGPLAIN' in os.environ
267 267
268 268 def username(self):
269 269 """Return default username to be used in commits.
270 270
271 271 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
272 272 and stop searching if one of these is set.
273 273 If not found and ui.askusername is True, ask the user, else use
274 274 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
275 275 """
276 276 user = os.environ.get("HGUSER")
277 277 if user is None:
278 278 user = self.config("ui", "username")
279 279 if user is not None:
280 280 user = os.path.expandvars(user)
281 281 if user is None:
282 282 user = os.environ.get("EMAIL")
283 283 if user is None and self.configbool("ui", "askusername"):
284 284 user = self.prompt(_("enter a commit username:"), default=None)
285 285 if user is None and not self.interactive():
286 286 try:
287 287 user = '%s@%s' % (util.getuser(), socket.getfqdn())
288 288 self.warn(_("No username found, using '%s' instead\n") % user)
289 289 except KeyError:
290 290 pass
291 291 if not user:
292 292 raise util.Abort(_('no username supplied (see "hg help config")'))
293 293 if "\n" in user:
294 294 raise util.Abort(_("username %s contains a newline\n") % repr(user))
295 295 return user
296 296
297 297 def shortuser(self, user):
298 298 """Return a short representation of a user name or email address."""
299 299 if not self.verbose:
300 300 user = util.shortuser(user)
301 301 return user
302 302
303 303 def _path(self, loc):
304 304 p = self.config('paths', loc)
305 305 if p:
306 306 if '%%' in p:
307 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
307 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n") %
308 308 (loc, p, self.configsource('paths', loc)))
309 309 p = p.replace('%%', '%')
310 310 p = util.expandpath(p)
311 311 return p
312 312
313 313 def expandpath(self, loc, default=None):
314 314 """Return repository location relative to cwd or from [paths]"""
315 315 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
316 316 return loc
317 317
318 318 path = self._path(loc)
319 319 if not path and default is not None:
320 320 path = self._path(default)
321 321 return path or loc
322 322
323 323 def pushbuffer(self):
324 324 self._buffers.append([])
325 325
326 326 def popbuffer(self, labeled=False):
327 327 '''pop the last buffer and return the buffered output
328 328
329 329 If labeled is True, any labels associated with buffered
330 330 output will be handled. By default, this has no effect
331 331 on the output returned, but extensions and GUI tools may
332 332 handle this argument and returned styled output. If output
333 333 is being buffered so it can be captured and parsed or
334 334 processed, labeled should not be set to True.
335 335 '''
336 336 return "".join(self._buffers.pop())
337 337
338 338 def write(self, *args, **opts):
339 339 '''write args to output
340 340
341 341 By default, this method simply writes to the buffer or stdout,
342 342 but extensions or GUI tools may override this method,
343 343 write_err(), popbuffer(), and label() to style output from
344 344 various parts of hg.
345 345
346 346 An optional keyword argument, "label", can be passed in.
347 347 This should be a string containing label names separated by
348 348 space. Label names take the form of "topic.type". For example,
349 349 ui.debug() issues a label of "ui.debug".
350 350
351 351 When labeling output for a specific command, a label of
352 352 "cmdname.type" is recommended. For example, status issues
353 353 a label of "status.modified" for modified files.
354 354 '''
355 355 if self._buffers:
356 356 self._buffers[-1].extend([str(a) for a in args])
357 357 else:
358 358 for a in args:
359 359 sys.stdout.write(str(a))
360 360
361 361 def write_err(self, *args, **opts):
362 362 try:
363 363 if not getattr(sys.stdout, 'closed', False):
364 364 sys.stdout.flush()
365 365 for a in args:
366 366 sys.stderr.write(str(a))
367 367 # stderr may be buffered under win32 when redirected to files,
368 368 # including stdout.
369 369 if not getattr(sys.stderr, 'closed', False):
370 370 sys.stderr.flush()
371 371 except IOError, inst:
372 372 if inst.errno not in (errno.EPIPE, errno.EIO):
373 373 raise
374 374
375 375 def flush(self):
376 376 try: sys.stdout.flush()
377 377 except: pass
378 378 try: sys.stderr.flush()
379 379 except: pass
380 380
381 381 def interactive(self):
382 382 '''is interactive input allowed?
383 383
384 384 An interactive session is a session where input can be reasonably read
385 385 from `sys.stdin'. If this function returns false, any attempt to read
386 386 from stdin should fail with an error, unless a sensible default has been
387 387 specified.
388 388
389 389 Interactiveness is triggered by the value of the `ui.interactive'
390 390 configuration variable or - if it is unset - when `sys.stdin' points
391 391 to a terminal device.
392 392
393 393 This function refers to input only; for output, see `ui.formatted()'.
394 394 '''
395 395 i = self.configbool("ui", "interactive", None)
396 396 if i is None:
397 397 try:
398 398 return sys.stdin.isatty()
399 399 except AttributeError:
400 400 # some environments replace stdin without implementing isatty
401 401 # usually those are non-interactive
402 402 return False
403 403
404 404 return i
405 405
406 406 def formatted(self):
407 407 '''should formatted output be used?
408 408
409 409 It is often desirable to format the output to suite the output medium.
410 410 Examples of this are truncating long lines or colorizing messages.
411 411 However, this is not often not desirable when piping output into other
412 412 utilities, e.g. `grep'.
413 413
414 414 Formatted output is triggered by the value of the `ui.formatted'
415 415 configuration variable or - if it is unset - when `sys.stdout' points
416 416 to a terminal device. Please note that `ui.formatted' should be
417 417 considered an implementation detail; it is not intended for use outside
418 418 Mercurial or its extensions.
419 419
420 420 This function refers to output only; for input, see `ui.interactive()'.
421 421 This function always returns false when in plain mode, see `ui.plain()'.
422 422 '''
423 423 if self.plain():
424 424 return False
425 425
426 426 i = self.configbool("ui", "formatted", None)
427 427 if i is None:
428 428 try:
429 429 return sys.stdout.isatty()
430 430 except AttributeError:
431 431 # some environments replace stdout without implementing isatty
432 432 # usually those are non-interactive
433 433 return False
434 434
435 435 return i
436 436
437 437 def _readline(self, prompt=''):
438 438 if sys.stdin.isatty():
439 439 try:
440 440 # magically add command line editing support, where
441 441 # available
442 442 import readline
443 443 # force demandimport to really load the module
444 444 readline.read_history_file
445 445 # windows sometimes raises something other than ImportError
446 446 except Exception:
447 447 pass
448 448 line = raw_input(prompt)
449 449 # When stdin is in binary mode on Windows, it can cause
450 450 # raw_input() to emit an extra trailing carriage return
451 451 if os.linesep == '\r\n' and line and line[-1] == '\r':
452 452 line = line[:-1]
453 453 return line
454 454
455 455 def prompt(self, msg, default="y"):
456 456 """Prompt user with msg, read response.
457 457 If ui is not interactive, the default is returned.
458 458 """
459 459 if not self.interactive():
460 460 self.write(msg, ' ', default, "\n")
461 461 return default
462 462 try:
463 463 r = self._readline(msg + ' ')
464 464 if not r:
465 465 return default
466 466 return r
467 467 except EOFError:
468 468 raise util.Abort(_('response expected'))
469 469
470 470 def promptchoice(self, msg, choices, default=0):
471 471 """Prompt user with msg, read response, and ensure it matches
472 472 one of the provided choices. The index of the choice is returned.
473 473 choices is a sequence of acceptable responses with the format:
474 474 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
475 475 If ui is not interactive, the default is returned.
476 476 """
477 477 resps = [s[s.index('&')+1].lower() for s in choices]
478 478 while True:
479 479 r = self.prompt(msg, resps[default])
480 480 if r.lower() in resps:
481 481 return resps.index(r.lower())
482 482 self.write(_("unrecognized response\n"))
483 483
484 484 def getpass(self, prompt=None, default=None):
485 485 if not self.interactive():
486 486 return default
487 487 try:
488 488 return getpass.getpass(prompt or _('password: '))
489 489 except EOFError:
490 490 raise util.Abort(_('response expected'))
491 491 def status(self, *msg, **opts):
492 492 '''write status message to output (if ui.quiet is False)
493 493
494 494 This adds an output label of "ui.status".
495 495 '''
496 496 if not self.quiet:
497 497 opts['label'] = opts.get('label', '') + ' ui.status'
498 498 self.write(*msg, **opts)
499 499 def warn(self, *msg, **opts):
500 500 '''write warning message to output (stderr)
501 501
502 502 This adds an output label of "ui.warning".
503 503 '''
504 504 opts['label'] = opts.get('label', '') + ' ui.warning'
505 505 self.write_err(*msg, **opts)
506 506 def note(self, *msg, **opts):
507 507 '''write note to output (if ui.verbose is True)
508 508
509 509 This adds an output label of "ui.note".
510 510 '''
511 511 if self.verbose:
512 512 opts['label'] = opts.get('label', '') + ' ui.note'
513 513 self.write(*msg, **opts)
514 514 def debug(self, *msg, **opts):
515 515 '''write debug message to output (if ui.debugflag is True)
516 516
517 517 This adds an output label of "ui.debug".
518 518 '''
519 519 if self.debugflag:
520 520 opts['label'] = opts.get('label', '') + ' ui.debug'
521 521 self.write(*msg, **opts)
522 522 def edit(self, text, user):
523 523 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
524 524 text=True)
525 525 try:
526 526 f = os.fdopen(fd, "w")
527 527 f.write(text)
528 528 f.close()
529 529
530 530 editor = self.geteditor()
531 531
532 532 util.system("%s \"%s\"" % (editor, name),
533 533 environ={'HGUSER': user},
534 534 onerr=util.Abort, errprefix=_("edit failed"))
535 535
536 536 f = open(name)
537 537 t = f.read()
538 538 f.close()
539 539 finally:
540 540 os.unlink(name)
541 541
542 542 return t
543 543
544 544 def traceback(self, exc=None):
545 545 '''print exception traceback if traceback printing enabled.
546 546 only to call in exception handler. returns true if traceback
547 547 printed.'''
548 548 if self.tracebackflag:
549 549 if exc:
550 550 traceback.print_exception(exc[0], exc[1], exc[2])
551 551 else:
552 552 traceback.print_exc()
553 553 return self.tracebackflag
554 554
555 555 def geteditor(self):
556 556 '''return editor to use'''
557 557 return (os.environ.get("HGEDITOR") or
558 558 self.config("ui", "editor") or
559 559 os.environ.get("VISUAL") or
560 560 os.environ.get("EDITOR", "vi"))
561 561
562 562 def progress(self, topic, pos, item="", unit="", total=None):
563 563 '''show a progress message
564 564
565 565 With stock hg, this is simply a debug message that is hidden
566 566 by default, but with extensions or GUI tools it may be
567 567 visible. 'topic' is the current operation, 'item' is a
568 568 non-numeric marker of the current position (ie the currently
569 569 in-process file), 'pos' is the current numeric position (ie
570 570 revision, bytes, etc.), unit is a corresponding unit label,
571 571 and total is the highest expected pos.
572 572
573 573 Multiple nested topics may be active at a time.
574 574
575 575 All topics should be marked closed by setting pos to None at
576 576 termination.
577 577 '''
578 578
579 579 if pos == None or not self.debugflag:
580 580 return
581 581
582 582 if unit:
583 583 unit = ' ' + unit
584 584 if item:
585 585 item = ' ' + item
586 586
587 587 if total:
588 588 pct = 100.0 * pos / total
589 589 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
590 590 % (topic, item, pos, total, unit, pct))
591 591 else:
592 592 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
593 593
594 594 def label(self, msg, label):
595 595 '''style msg based on supplied label
596 596
597 597 Like ui.write(), this just returns msg unchanged, but extensions
598 598 and GUI tools can override it to allow styling output without
599 599 writing it.
600 600
601 601 ui.write(s, 'label') is equivalent to
602 602 ui.write(ui.label(s, 'label')).
603 603 '''
604 604 return msg
General Comments 0
You need to be logged in to leave comments. Login now