##// END OF EJS Templates
ui: just return it if it's already a bool
Dirkjan Ochtman -
r10243:cd3e5c47 stable
parent child Browse files
Show More
@@ -1,386 +1,388 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, incorporated herein by reference.
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.fixconfig()
33 33 else:
34 34 # we always trust global config files
35 35 for f in util.rcpath():
36 36 self.readconfig(f, trust=True)
37 37
38 38 def copy(self):
39 39 return self.__class__(self)
40 40
41 41 def _is_trusted(self, fp, f):
42 42 st = util.fstat(fp)
43 43 if util.isowner(st):
44 44 return True
45 45
46 46 tusers, tgroups = self._trustusers, self._trustgroups
47 47 if '*' in tusers or '*' in tgroups:
48 48 return True
49 49
50 50 user = util.username(st.st_uid)
51 51 group = util.groupname(st.st_gid)
52 52 if user in tusers or group in tgroups or user == util.username():
53 53 return True
54 54
55 55 if self._reportuntrusted:
56 56 self.warn(_('Not trusting file %s from untrusted '
57 57 'user %s, group %s\n') % (f, user, group))
58 58 return False
59 59
60 60 def readconfig(self, filename, root=None, trust=False,
61 61 sections=None, remap=None):
62 62 try:
63 63 fp = open(filename)
64 64 except IOError:
65 65 if not sections: # ignore unless we were looking for something
66 66 return
67 67 raise
68 68
69 69 cfg = config.config()
70 70 trusted = sections or trust or self._is_trusted(fp, filename)
71 71
72 72 try:
73 73 cfg.read(filename, fp, sections=sections, remap=remap)
74 74 except error.ConfigError, inst:
75 75 if trusted:
76 76 raise
77 77 self.warn(_("Ignored: %s\n") % str(inst))
78 78
79 79 if trusted:
80 80 self._tcfg.update(cfg)
81 81 self._tcfg.update(self._ocfg)
82 82 self._ucfg.update(cfg)
83 83 self._ucfg.update(self._ocfg)
84 84
85 85 if root is None:
86 86 root = os.path.expanduser('~')
87 87 self.fixconfig(root=root)
88 88
89 89 def fixconfig(self, root=None):
90 90 # translate paths relative to root (or home) into absolute paths
91 91 root = root or os.getcwd()
92 92 for c in self._tcfg, self._ucfg, self._ocfg:
93 93 for n, p in c.items('paths'):
94 94 if p and "://" not in p and not os.path.isabs(p):
95 95 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
96 96
97 97 # update ui options
98 98 self.debugflag = self.configbool('ui', 'debug')
99 99 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
100 100 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
101 101 if self.verbose and self.quiet:
102 102 self.quiet = self.verbose = False
103 103 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
104 104 self.tracebackflag = self.configbool('ui', 'traceback', False)
105 105
106 106 # update trust information
107 107 self._trustusers.update(self.configlist('trusted', 'users'))
108 108 self._trustgroups.update(self.configlist('trusted', 'groups'))
109 109
110 110 def setconfig(self, section, name, value):
111 111 for cfg in (self._ocfg, self._tcfg, self._ucfg):
112 112 cfg.set(section, name, value)
113 113 self.fixconfig()
114 114
115 115 def _data(self, untrusted):
116 116 return untrusted and self._ucfg or self._tcfg
117 117
118 118 def configsource(self, section, name, untrusted=False):
119 119 return self._data(untrusted).source(section, name) or 'none'
120 120
121 121 def config(self, section, name, default=None, untrusted=False):
122 122 value = self._data(untrusted).get(section, name, default)
123 123 if self.debugflag and not untrusted and self._reportuntrusted:
124 124 uvalue = self._ucfg.get(section, name)
125 125 if uvalue is not None and uvalue != value:
126 126 self.debug(_("ignoring untrusted configuration option "
127 127 "%s.%s = %s\n") % (section, name, uvalue))
128 128 return value
129 129
130 130 def configbool(self, section, name, default=False, untrusted=False):
131 131 v = self.config(section, name, None, untrusted)
132 132 if v is None:
133 133 return default
134 if isinstance(v, bool):
135 return v
134 136 if v.lower() not in _booleans:
135 137 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
136 138 % (section, name, v))
137 139 return _booleans[v.lower()]
138 140
139 141 def configlist(self, section, name, default=None, untrusted=False):
140 142 """Return a list of comma/space separated strings"""
141 143 result = self.config(section, name, untrusted=untrusted)
142 144 if result is None:
143 145 result = default or []
144 146 if isinstance(result, basestring):
145 147 result = result.replace(",", " ").split()
146 148 return result
147 149
148 150 def has_section(self, section, untrusted=False):
149 151 '''tell whether section exists in config.'''
150 152 return section in self._data(untrusted)
151 153
152 154 def configitems(self, section, untrusted=False):
153 155 items = self._data(untrusted).items(section)
154 156 if self.debugflag and not untrusted and self._reportuntrusted:
155 157 for k, v in self._ucfg.items(section):
156 158 if self._tcfg.get(section, k) != v:
157 159 self.debug(_("ignoring untrusted configuration option "
158 160 "%s.%s = %s\n") % (section, k, v))
159 161 return items
160 162
161 163 def walkconfig(self, untrusted=False):
162 164 cfg = self._data(untrusted)
163 165 for section in cfg.sections():
164 166 for name, value in self.configitems(section, untrusted):
165 167 yield section, name, str(value).replace('\n', '\\n')
166 168
167 169 def username(self):
168 170 """Return default username to be used in commits.
169 171
170 172 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
171 173 and stop searching if one of these is set.
172 174 If not found and ui.askusername is True, ask the user, else use
173 175 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
174 176 """
175 177 user = os.environ.get("HGUSER")
176 178 if user is None:
177 179 user = self.config("ui", "username")
178 180 if user is None:
179 181 user = os.environ.get("EMAIL")
180 182 if user is None and self.configbool("ui", "askusername"):
181 183 user = self.prompt(_("enter a commit username:"), default=None)
182 184 if user is None and not self.interactive():
183 185 try:
184 186 user = '%s@%s' % (util.getuser(), socket.getfqdn())
185 187 self.warn(_("No username found, using '%s' instead\n") % user)
186 188 except KeyError:
187 189 pass
188 190 if not user:
189 191 raise util.Abort(_('no username supplied (see "hg help config")'))
190 192 if "\n" in user:
191 193 raise util.Abort(_("username %s contains a newline\n") % repr(user))
192 194 return user
193 195
194 196 def shortuser(self, user):
195 197 """Return a short representation of a user name or email address."""
196 198 if not self.verbose: user = util.shortuser(user)
197 199 return user
198 200
199 201 def _path(self, loc):
200 202 p = self.config('paths', loc)
201 203 if p:
202 204 if '%%' in p:
203 205 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
204 206 (loc, p, self.configsource('paths', loc)))
205 207 p = p.replace('%%', '%')
206 208 p = util.expandpath(p)
207 209 return p
208 210
209 211 def expandpath(self, loc, default=None):
210 212 """Return repository location relative to cwd or from [paths]"""
211 213 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
212 214 return loc
213 215
214 216 path = self._path(loc)
215 217 if not path and default is not None:
216 218 path = self._path(default)
217 219 return path or loc
218 220
219 221 def pushbuffer(self):
220 222 self._buffers.append([])
221 223
222 224 def popbuffer(self):
223 225 return "".join(self._buffers.pop())
224 226
225 227 def write(self, *args):
226 228 if self._buffers:
227 229 self._buffers[-1].extend([str(a) for a in args])
228 230 else:
229 231 for a in args:
230 232 sys.stdout.write(str(a))
231 233
232 234 def write_err(self, *args):
233 235 try:
234 236 if not sys.stdout.closed: sys.stdout.flush()
235 237 for a in args:
236 238 sys.stderr.write(str(a))
237 239 # stderr may be buffered under win32 when redirected to files,
238 240 # including stdout.
239 241 if not sys.stderr.closed: sys.stderr.flush()
240 242 except IOError, inst:
241 243 if inst.errno != errno.EPIPE:
242 244 raise
243 245
244 246 def flush(self):
245 247 try: sys.stdout.flush()
246 248 except: pass
247 249 try: sys.stderr.flush()
248 250 except: pass
249 251
250 252 def interactive(self):
251 253 i = self.configbool("ui", "interactive", None)
252 254 if i is None:
253 255 return sys.stdin.isatty()
254 256 return i
255 257
256 258 def _readline(self, prompt=''):
257 259 if sys.stdin.isatty():
258 260 try:
259 261 # magically add command line editing support, where
260 262 # available
261 263 import readline
262 264 # force demandimport to really load the module
263 265 readline.read_history_file
264 266 # windows sometimes raises something other than ImportError
265 267 except Exception:
266 268 pass
267 269 line = raw_input(prompt)
268 270 # When stdin is in binary mode on Windows, it can cause
269 271 # raw_input() to emit an extra trailing carriage return
270 272 if os.linesep == '\r\n' and line and line[-1] == '\r':
271 273 line = line[:-1]
272 274 return line
273 275
274 276 def prompt(self, msg, default="y"):
275 277 """Prompt user with msg, read response.
276 278 If ui is not interactive, the default is returned.
277 279 """
278 280 if not self.interactive():
279 281 self.write(msg, ' ', default, "\n")
280 282 return default
281 283 try:
282 284 r = self._readline(msg + ' ')
283 285 if not r:
284 286 return default
285 287 return r
286 288 except EOFError:
287 289 raise util.Abort(_('response expected'))
288 290
289 291 def promptchoice(self, msg, choices, default=0):
290 292 """Prompt user with msg, read response, and ensure it matches
291 293 one of the provided choices. The index of the choice is returned.
292 294 choices is a sequence of acceptable responses with the format:
293 295 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
294 296 If ui is not interactive, the default is returned.
295 297 """
296 298 resps = [s[s.index('&')+1].lower() for s in choices]
297 299 while True:
298 300 r = self.prompt(msg, resps[default])
299 301 if r.lower() in resps:
300 302 return resps.index(r.lower())
301 303 self.write(_("unrecognized response\n"))
302 304
303 305
304 306 def getpass(self, prompt=None, default=None):
305 307 if not self.interactive(): return default
306 308 try:
307 309 return getpass.getpass(prompt or _('password: '))
308 310 except EOFError:
309 311 raise util.Abort(_('response expected'))
310 312 def status(self, *msg):
311 313 if not self.quiet: self.write(*msg)
312 314 def warn(self, *msg):
313 315 self.write_err(*msg)
314 316 def note(self, *msg):
315 317 if self.verbose: self.write(*msg)
316 318 def debug(self, *msg):
317 319 if self.debugflag: self.write(*msg)
318 320 def edit(self, text, user):
319 321 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
320 322 text=True)
321 323 try:
322 324 f = os.fdopen(fd, "w")
323 325 f.write(text)
324 326 f.close()
325 327
326 328 editor = self.geteditor()
327 329
328 330 util.system("%s \"%s\"" % (editor, name),
329 331 environ={'HGUSER': user},
330 332 onerr=util.Abort, errprefix=_("edit failed"))
331 333
332 334 f = open(name)
333 335 t = f.read()
334 336 f.close()
335 337 finally:
336 338 os.unlink(name)
337 339
338 340 return t
339 341
340 342 def traceback(self, exc=None):
341 343 '''print exception traceback if traceback printing enabled.
342 344 only to call in exception handler. returns true if traceback
343 345 printed.'''
344 346 if self.tracebackflag:
345 347 if exc:
346 348 traceback.print_exception(exc[0], exc[1], exc[2])
347 349 else:
348 350 traceback.print_exc()
349 351 return self.tracebackflag
350 352
351 353 def geteditor(self):
352 354 '''return editor to use'''
353 355 return (os.environ.get("HGEDITOR") or
354 356 self.config("ui", "editor") or
355 357 os.environ.get("VISUAL") or
356 358 os.environ.get("EDITOR", "vi"))
357 359
358 360 def progress(self, topic, pos, item="", unit="", total=None):
359 361 '''show a progress message
360 362
361 363 With stock hg, this is simply a debug message that is hidden
362 364 by default, but with extensions or GUI tools it may be
363 365 visible. 'topic' is the current operation, 'item' is a
364 366 non-numeric marker of the current position (ie the currently
365 367 in-process file), 'pos' is the current numeric position (ie
366 368 revision, bytes, etc.), unit is a corresponding unit label,
367 369 and total is the highest expected pos.
368 370
369 371 Multiple nested topics may be active at a time. All topics
370 372 should be marked closed by setting pos to None at termination.
371 373 '''
372 374
373 375 if pos == None or not self.debugflag:
374 376 return
375 377
376 378 if unit:
377 379 unit = ' ' + unit
378 380 if item:
379 381 item = ' ' + item
380 382
381 383 if total:
382 384 pct = 100.0 * pos / total
383 385 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
384 386 % (topic, item, pos, total, unit, pct))
385 387 else:
386 388 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
General Comments 0
You need to be logged in to leave comments. Login now