##// END OF EJS Templates
ui: add a configbytes method, for space configuration...
Bryan O'Sullivan -
r19065:2c4cd1c4 default
parent child Browse files
Show More
@@ -1,773 +1,812 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 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, re, socket, sys, tempfile, traceback
10 10 import config, scmutil, util, error, formatter
11 11
12 12 class ui(object):
13 13 def __init__(self, src=None):
14 14 self._buffers = []
15 15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 16 self._reportuntrusted = True
17 17 self._ocfg = config.config() # overlay
18 18 self._tcfg = config.config() # trusted
19 19 self._ucfg = config.config() # untrusted
20 20 self._trustusers = set()
21 21 self._trustgroups = set()
22 22 self.callhooks = True
23 23
24 24 if src:
25 25 self.fout = src.fout
26 26 self.ferr = src.ferr
27 27 self.fin = src.fin
28 28
29 29 self._tcfg = src._tcfg.copy()
30 30 self._ucfg = src._ucfg.copy()
31 31 self._ocfg = src._ocfg.copy()
32 32 self._trustusers = src._trustusers.copy()
33 33 self._trustgroups = src._trustgroups.copy()
34 34 self.environ = src.environ
35 35 self.callhooks = src.callhooks
36 36 self.fixconfig()
37 37 else:
38 38 self.fout = sys.stdout
39 39 self.ferr = sys.stderr
40 40 self.fin = sys.stdin
41 41
42 42 # shared read-only environment
43 43 self.environ = os.environ
44 44 # we always trust global config files
45 45 for f in scmutil.rcpath():
46 46 self.readconfig(f, trust=True)
47 47
48 48 def copy(self):
49 49 return self.__class__(self)
50 50
51 51 def formatter(self, topic, opts):
52 52 return formatter.formatter(self, topic, opts)
53 53
54 54 def _trusted(self, fp, f):
55 55 st = util.fstat(fp)
56 56 if util.isowner(st):
57 57 return True
58 58
59 59 tusers, tgroups = self._trustusers, self._trustgroups
60 60 if '*' in tusers or '*' in tgroups:
61 61 return True
62 62
63 63 user = util.username(st.st_uid)
64 64 group = util.groupname(st.st_gid)
65 65 if user in tusers or group in tgroups or user == util.username():
66 66 return True
67 67
68 68 if self._reportuntrusted:
69 69 self.warn(_('not trusting file %s from untrusted '
70 70 'user %s, group %s\n') % (f, user, group))
71 71 return False
72 72
73 73 def readconfig(self, filename, root=None, trust=False,
74 74 sections=None, remap=None):
75 75 try:
76 76 fp = open(filename)
77 77 except IOError:
78 78 if not sections: # ignore unless we were looking for something
79 79 return
80 80 raise
81 81
82 82 cfg = config.config()
83 83 trusted = sections or trust or self._trusted(fp, filename)
84 84
85 85 try:
86 86 cfg.read(filename, fp, sections=sections, remap=remap)
87 87 fp.close()
88 88 except error.ConfigError, inst:
89 89 if trusted:
90 90 raise
91 91 self.warn(_("ignored: %s\n") % str(inst))
92 92
93 93 if self.plain():
94 94 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
95 95 'logtemplate', 'style',
96 96 'traceback', 'verbose'):
97 97 if k in cfg['ui']:
98 98 del cfg['ui'][k]
99 99 for k, v in cfg.items('defaults'):
100 100 del cfg['defaults'][k]
101 101 # Don't remove aliases from the configuration if in the exceptionlist
102 102 if self.plain('alias'):
103 103 for k, v in cfg.items('alias'):
104 104 del cfg['alias'][k]
105 105
106 106 if trusted:
107 107 self._tcfg.update(cfg)
108 108 self._tcfg.update(self._ocfg)
109 109 self._ucfg.update(cfg)
110 110 self._ucfg.update(self._ocfg)
111 111
112 112 if root is None:
113 113 root = os.path.expanduser('~')
114 114 self.fixconfig(root=root)
115 115
116 116 def fixconfig(self, root=None, section=None):
117 117 if section in (None, 'paths'):
118 118 # expand vars and ~
119 119 # translate paths relative to root (or home) into absolute paths
120 120 root = root or os.getcwd()
121 121 for c in self._tcfg, self._ucfg, self._ocfg:
122 122 for n, p in c.items('paths'):
123 123 if not p:
124 124 continue
125 125 if '%%' in p:
126 126 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
127 127 % (n, p, self.configsource('paths', n)))
128 128 p = p.replace('%%', '%')
129 129 p = util.expandpath(p)
130 130 if not util.hasscheme(p) and not os.path.isabs(p):
131 131 p = os.path.normpath(os.path.join(root, p))
132 132 c.set("paths", n, p)
133 133
134 134 if section in (None, 'ui'):
135 135 # update ui options
136 136 self.debugflag = self.configbool('ui', 'debug')
137 137 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
138 138 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
139 139 if self.verbose and self.quiet:
140 140 self.quiet = self.verbose = False
141 141 self._reportuntrusted = self.debugflag or self.configbool("ui",
142 142 "report_untrusted", True)
143 143 self.tracebackflag = self.configbool('ui', 'traceback', False)
144 144
145 145 if section in (None, 'trusted'):
146 146 # update trust information
147 147 self._trustusers.update(self.configlist('trusted', 'users'))
148 148 self._trustgroups.update(self.configlist('trusted', 'groups'))
149 149
150 150 def backupconfig(self, section, item):
151 151 return (self._ocfg.backup(section, item),
152 152 self._tcfg.backup(section, item),
153 153 self._ucfg.backup(section, item),)
154 154 def restoreconfig(self, data):
155 155 self._ocfg.restore(data[0])
156 156 self._tcfg.restore(data[1])
157 157 self._ucfg.restore(data[2])
158 158
159 159 def setconfig(self, section, name, value, overlay=True):
160 160 if overlay:
161 161 self._ocfg.set(section, name, value)
162 162 self._tcfg.set(section, name, value)
163 163 self._ucfg.set(section, name, value)
164 164 self.fixconfig(section=section)
165 165
166 166 def _data(self, untrusted):
167 167 return untrusted and self._ucfg or self._tcfg
168 168
169 169 def configsource(self, section, name, untrusted=False):
170 170 return self._data(untrusted).source(section, name) or 'none'
171 171
172 172 def config(self, section, name, default=None, untrusted=False):
173 173 if isinstance(name, list):
174 174 alternates = name
175 175 else:
176 176 alternates = [name]
177 177
178 178 for n in alternates:
179 179 value = self._data(untrusted).get(section, name, None)
180 180 if value is not None:
181 181 name = n
182 182 break
183 183 else:
184 184 value = default
185 185
186 186 if self.debugflag and not untrusted and self._reportuntrusted:
187 187 uvalue = self._ucfg.get(section, name)
188 188 if uvalue is not None and uvalue != value:
189 189 self.debug("ignoring untrusted configuration option "
190 190 "%s.%s = %s\n" % (section, name, uvalue))
191 191 return value
192 192
193 193 def configpath(self, section, name, default=None, untrusted=False):
194 194 'get a path config item, expanded relative to repo root or config file'
195 195 v = self.config(section, name, default, untrusted)
196 196 if v is None:
197 197 return None
198 198 if not os.path.isabs(v) or "://" not in v:
199 199 src = self.configsource(section, name, untrusted)
200 200 if ':' in src:
201 201 base = os.path.dirname(src.rsplit(':')[0])
202 202 v = os.path.join(base, os.path.expanduser(v))
203 203 return v
204 204
205 205 def configbool(self, section, name, default=False, untrusted=False):
206 206 """parse a configuration element as a boolean
207 207
208 208 >>> u = ui(); s = 'foo'
209 209 >>> u.setconfig(s, 'true', 'yes')
210 210 >>> u.configbool(s, 'true')
211 211 True
212 212 >>> u.setconfig(s, 'false', 'no')
213 213 >>> u.configbool(s, 'false')
214 214 False
215 215 >>> u.configbool(s, 'unknown')
216 216 False
217 217 >>> u.configbool(s, 'unknown', True)
218 218 True
219 219 >>> u.setconfig(s, 'invalid', 'somevalue')
220 220 >>> u.configbool(s, 'invalid')
221 221 Traceback (most recent call last):
222 222 ...
223 223 ConfigError: foo.invalid is not a boolean ('somevalue')
224 224 """
225 225
226 226 v = self.config(section, name, None, untrusted)
227 227 if v is None:
228 228 return default
229 229 if isinstance(v, bool):
230 230 return v
231 231 b = util.parsebool(v)
232 232 if b is None:
233 233 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
234 234 % (section, name, v))
235 235 return b
236 236
237 237 def configint(self, section, name, default=None, untrusted=False):
238 238 """parse a configuration element as an integer
239 239
240 240 >>> u = ui(); s = 'foo'
241 241 >>> u.setconfig(s, 'int1', '42')
242 242 >>> u.configint(s, 'int1')
243 243 42
244 244 >>> u.setconfig(s, 'int2', '-42')
245 245 >>> u.configint(s, 'int2')
246 246 -42
247 247 >>> u.configint(s, 'unknown', 7)
248 248 7
249 249 >>> u.setconfig(s, 'invalid', 'somevalue')
250 250 >>> u.configint(s, 'invalid')
251 251 Traceback (most recent call last):
252 252 ...
253 253 ConfigError: foo.invalid is not an integer ('somevalue')
254 254 """
255 255
256 256 v = self.config(section, name, None, untrusted)
257 257 if v is None:
258 258 return default
259 259 try:
260 260 return int(v)
261 261 except ValueError:
262 262 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
263 263 % (section, name, v))
264 264
265 def configbytes(self, section, name, default=0, untrusted=False):
266 """parse a configuration element as a quantity in bytes
267
268 Units can be specified as b (bytes), k or kb (kilobytes), m or
269 mb (megabytes), g or gb (gigabytes).
270
271 >>> u = ui(); s = 'foo'
272 >>> u.setconfig(s, 'val1', '42')
273 >>> u.configbytes(s, 'val1')
274 42
275 >>> u.setconfig(s, 'val2', '42.5 kb')
276 >>> u.configbytes(s, 'val2')
277 43520
278 >>> u.configbytes(s, 'unknown', '7 MB')
279 7340032
280 >>> u.setconfig(s, 'invalid', 'somevalue')
281 >>> u.configbytes(s, 'invalid')
282 Traceback (most recent call last):
283 ...
284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
285 """
286
287 orig = string = self.config(section, name)
288 if orig is None:
289 if not isinstance(default, str):
290 return default
291 orig = string = default
292 multiple = 1
293 m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I)
294 if m:
295 string, key = m.groups()
296 key = key.lower()
297 multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1)
298 try:
299 return int(float(string) * multiple)
300 except ValueError:
301 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
302 % (section, name, orig))
303
265 304 def configlist(self, section, name, default=None, untrusted=False):
266 305 """parse a configuration element as a list of comma/space separated
267 306 strings
268 307
269 308 >>> u = ui(); s = 'foo'
270 309 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
271 310 >>> u.configlist(s, 'list1')
272 311 ['this', 'is', 'a small', 'test']
273 312 """
274 313
275 314 def _parse_plain(parts, s, offset):
276 315 whitespace = False
277 316 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
278 317 whitespace = True
279 318 offset += 1
280 319 if offset >= len(s):
281 320 return None, parts, offset
282 321 if whitespace:
283 322 parts.append('')
284 323 if s[offset] == '"' and not parts[-1]:
285 324 return _parse_quote, parts, offset + 1
286 325 elif s[offset] == '"' and parts[-1][-1] == '\\':
287 326 parts[-1] = parts[-1][:-1] + s[offset]
288 327 return _parse_plain, parts, offset + 1
289 328 parts[-1] += s[offset]
290 329 return _parse_plain, parts, offset + 1
291 330
292 331 def _parse_quote(parts, s, offset):
293 332 if offset < len(s) and s[offset] == '"': # ""
294 333 parts.append('')
295 334 offset += 1
296 335 while offset < len(s) and (s[offset].isspace() or
297 336 s[offset] == ','):
298 337 offset += 1
299 338 return _parse_plain, parts, offset
300 339
301 340 while offset < len(s) and s[offset] != '"':
302 341 if (s[offset] == '\\' and offset + 1 < len(s)
303 342 and s[offset + 1] == '"'):
304 343 offset += 1
305 344 parts[-1] += '"'
306 345 else:
307 346 parts[-1] += s[offset]
308 347 offset += 1
309 348
310 349 if offset >= len(s):
311 350 real_parts = _configlist(parts[-1])
312 351 if not real_parts:
313 352 parts[-1] = '"'
314 353 else:
315 354 real_parts[0] = '"' + real_parts[0]
316 355 parts = parts[:-1]
317 356 parts.extend(real_parts)
318 357 return None, parts, offset
319 358
320 359 offset += 1
321 360 while offset < len(s) and s[offset] in [' ', ',']:
322 361 offset += 1
323 362
324 363 if offset < len(s):
325 364 if offset + 1 == len(s) and s[offset] == '"':
326 365 parts[-1] += '"'
327 366 offset += 1
328 367 else:
329 368 parts.append('')
330 369 else:
331 370 return None, parts, offset
332 371
333 372 return _parse_plain, parts, offset
334 373
335 374 def _configlist(s):
336 375 s = s.rstrip(' ,')
337 376 if not s:
338 377 return []
339 378 parser, parts, offset = _parse_plain, [''], 0
340 379 while parser:
341 380 parser, parts, offset = parser(parts, s, offset)
342 381 return parts
343 382
344 383 result = self.config(section, name, untrusted=untrusted)
345 384 if result is None:
346 385 result = default or []
347 386 if isinstance(result, basestring):
348 387 result = _configlist(result.lstrip(' ,\n'))
349 388 if result is None:
350 389 result = default or []
351 390 return result
352 391
353 392 def has_section(self, section, untrusted=False):
354 393 '''tell whether section exists in config.'''
355 394 return section in self._data(untrusted)
356 395
357 396 def configitems(self, section, untrusted=False):
358 397 items = self._data(untrusted).items(section)
359 398 if self.debugflag and not untrusted and self._reportuntrusted:
360 399 for k, v in self._ucfg.items(section):
361 400 if self._tcfg.get(section, k) != v:
362 401 self.debug("ignoring untrusted configuration option "
363 402 "%s.%s = %s\n" % (section, k, v))
364 403 return items
365 404
366 405 def walkconfig(self, untrusted=False):
367 406 cfg = self._data(untrusted)
368 407 for section in cfg.sections():
369 408 for name, value in self.configitems(section, untrusted):
370 409 yield section, name, value
371 410
372 411 def plain(self, feature=None):
373 412 '''is plain mode active?
374 413
375 414 Plain mode means that all configuration variables which affect
376 415 the behavior and output of Mercurial should be
377 416 ignored. Additionally, the output should be stable,
378 417 reproducible and suitable for use in scripts or applications.
379 418
380 419 The only way to trigger plain mode is by setting either the
381 420 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
382 421
383 422 The return value can either be
384 423 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
385 424 - True otherwise
386 425 '''
387 426 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
388 427 return False
389 428 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
390 429 if feature and exceptions:
391 430 return feature not in exceptions
392 431 return True
393 432
394 433 def username(self):
395 434 """Return default username to be used in commits.
396 435
397 436 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
398 437 and stop searching if one of these is set.
399 438 If not found and ui.askusername is True, ask the user, else use
400 439 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
401 440 """
402 441 user = os.environ.get("HGUSER")
403 442 if user is None:
404 443 user = self.config("ui", "username")
405 444 if user is not None:
406 445 user = os.path.expandvars(user)
407 446 if user is None:
408 447 user = os.environ.get("EMAIL")
409 448 if user is None and self.configbool("ui", "askusername"):
410 449 user = self.prompt(_("enter a commit username:"), default=None)
411 450 if user is None and not self.interactive():
412 451 try:
413 452 user = '%s@%s' % (util.getuser(), socket.getfqdn())
414 453 self.warn(_("no username found, using '%s' instead\n") % user)
415 454 except KeyError:
416 455 pass
417 456 if not user:
418 457 raise util.Abort(_('no username supplied (see "hg help config")'))
419 458 if "\n" in user:
420 459 raise util.Abort(_("username %s contains a newline\n") % repr(user))
421 460 return user
422 461
423 462 def shortuser(self, user):
424 463 """Return a short representation of a user name or email address."""
425 464 if not self.verbose:
426 465 user = util.shortuser(user)
427 466 return user
428 467
429 468 def expandpath(self, loc, default=None):
430 469 """Return repository location relative to cwd or from [paths]"""
431 470 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
432 471 return loc
433 472
434 473 path = self.config('paths', loc)
435 474 if not path and default is not None:
436 475 path = self.config('paths', default)
437 476 return path or loc
438 477
439 478 def pushbuffer(self):
440 479 self._buffers.append([])
441 480
442 481 def popbuffer(self, labeled=False):
443 482 '''pop the last buffer and return the buffered output
444 483
445 484 If labeled is True, any labels associated with buffered
446 485 output will be handled. By default, this has no effect
447 486 on the output returned, but extensions and GUI tools may
448 487 handle this argument and returned styled output. If output
449 488 is being buffered so it can be captured and parsed or
450 489 processed, labeled should not be set to True.
451 490 '''
452 491 return "".join(self._buffers.pop())
453 492
454 493 def write(self, *args, **opts):
455 494 '''write args to output
456 495
457 496 By default, this method simply writes to the buffer or stdout,
458 497 but extensions or GUI tools may override this method,
459 498 write_err(), popbuffer(), and label() to style output from
460 499 various parts of hg.
461 500
462 501 An optional keyword argument, "label", can be passed in.
463 502 This should be a string containing label names separated by
464 503 space. Label names take the form of "topic.type". For example,
465 504 ui.debug() issues a label of "ui.debug".
466 505
467 506 When labeling output for a specific command, a label of
468 507 "cmdname.type" is recommended. For example, status issues
469 508 a label of "status.modified" for modified files.
470 509 '''
471 510 if self._buffers:
472 511 self._buffers[-1].extend([str(a) for a in args])
473 512 else:
474 513 for a in args:
475 514 self.fout.write(str(a))
476 515
477 516 def write_err(self, *args, **opts):
478 517 try:
479 518 if not getattr(self.fout, 'closed', False):
480 519 self.fout.flush()
481 520 for a in args:
482 521 self.ferr.write(str(a))
483 522 # stderr may be buffered under win32 when redirected to files,
484 523 # including stdout.
485 524 if not getattr(self.ferr, 'closed', False):
486 525 self.ferr.flush()
487 526 except IOError, inst:
488 527 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
489 528 raise
490 529
491 530 def flush(self):
492 531 try: self.fout.flush()
493 532 except (IOError, ValueError): pass
494 533 try: self.ferr.flush()
495 534 except (IOError, ValueError): pass
496 535
497 536 def _isatty(self, fh):
498 537 if self.configbool('ui', 'nontty', False):
499 538 return False
500 539 return util.isatty(fh)
501 540
502 541 def interactive(self):
503 542 '''is interactive input allowed?
504 543
505 544 An interactive session is a session where input can be reasonably read
506 545 from `sys.stdin'. If this function returns false, any attempt to read
507 546 from stdin should fail with an error, unless a sensible default has been
508 547 specified.
509 548
510 549 Interactiveness is triggered by the value of the `ui.interactive'
511 550 configuration variable or - if it is unset - when `sys.stdin' points
512 551 to a terminal device.
513 552
514 553 This function refers to input only; for output, see `ui.formatted()'.
515 554 '''
516 555 i = self.configbool("ui", "interactive", None)
517 556 if i is None:
518 557 # some environments replace stdin without implementing isatty
519 558 # usually those are non-interactive
520 559 return self._isatty(self.fin)
521 560
522 561 return i
523 562
524 563 def termwidth(self):
525 564 '''how wide is the terminal in columns?
526 565 '''
527 566 if 'COLUMNS' in os.environ:
528 567 try:
529 568 return int(os.environ['COLUMNS'])
530 569 except ValueError:
531 570 pass
532 571 return util.termwidth()
533 572
534 573 def formatted(self):
535 574 '''should formatted output be used?
536 575
537 576 It is often desirable to format the output to suite the output medium.
538 577 Examples of this are truncating long lines or colorizing messages.
539 578 However, this is not often not desirable when piping output into other
540 579 utilities, e.g. `grep'.
541 580
542 581 Formatted output is triggered by the value of the `ui.formatted'
543 582 configuration variable or - if it is unset - when `sys.stdout' points
544 583 to a terminal device. Please note that `ui.formatted' should be
545 584 considered an implementation detail; it is not intended for use outside
546 585 Mercurial or its extensions.
547 586
548 587 This function refers to output only; for input, see `ui.interactive()'.
549 588 This function always returns false when in plain mode, see `ui.plain()'.
550 589 '''
551 590 if self.plain():
552 591 return False
553 592
554 593 i = self.configbool("ui", "formatted", None)
555 594 if i is None:
556 595 # some environments replace stdout without implementing isatty
557 596 # usually those are non-interactive
558 597 return self._isatty(self.fout)
559 598
560 599 return i
561 600
562 601 def _readline(self, prompt=''):
563 602 if self._isatty(self.fin):
564 603 try:
565 604 # magically add command line editing support, where
566 605 # available
567 606 import readline
568 607 # force demandimport to really load the module
569 608 readline.read_history_file
570 609 # windows sometimes raises something other than ImportError
571 610 except Exception:
572 611 pass
573 612
574 613 # call write() so output goes through subclassed implementation
575 614 # e.g. color extension on Windows
576 615 self.write(prompt)
577 616
578 617 # instead of trying to emulate raw_input, swap (self.fin,
579 618 # self.fout) with (sys.stdin, sys.stdout)
580 619 oldin = sys.stdin
581 620 oldout = sys.stdout
582 621 sys.stdin = self.fin
583 622 sys.stdout = self.fout
584 623 line = raw_input(' ')
585 624 sys.stdin = oldin
586 625 sys.stdout = oldout
587 626
588 627 # When stdin is in binary mode on Windows, it can cause
589 628 # raw_input() to emit an extra trailing carriage return
590 629 if os.linesep == '\r\n' and line and line[-1] == '\r':
591 630 line = line[:-1]
592 631 return line
593 632
594 633 def prompt(self, msg, default="y"):
595 634 """Prompt user with msg, read response.
596 635 If ui is not interactive, the default is returned.
597 636 """
598 637 if not self.interactive():
599 638 self.write(msg, ' ', default, "\n")
600 639 return default
601 640 try:
602 641 r = self._readline(self.label(msg, 'ui.prompt'))
603 642 if not r:
604 643 return default
605 644 return r
606 645 except EOFError:
607 646 raise util.Abort(_('response expected'))
608 647
609 648 def promptchoice(self, msg, choices, default=0):
610 649 """Prompt user with msg, read response, and ensure it matches
611 650 one of the provided choices. The index of the choice is returned.
612 651 choices is a sequence of acceptable responses with the format:
613 652 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
614 653 If ui is not interactive, the default is returned.
615 654 """
616 655 resps = [s[s.index('&') + 1].lower() for s in choices]
617 656 while True:
618 657 r = self.prompt(msg, resps[default])
619 658 if r.lower() in resps:
620 659 return resps.index(r.lower())
621 660 self.write(_("unrecognized response\n"))
622 661
623 662 def getpass(self, prompt=None, default=None):
624 663 if not self.interactive():
625 664 return default
626 665 try:
627 666 return getpass.getpass(prompt or _('password: '))
628 667 except EOFError:
629 668 raise util.Abort(_('response expected'))
630 669 def status(self, *msg, **opts):
631 670 '''write status message to output (if ui.quiet is False)
632 671
633 672 This adds an output label of "ui.status".
634 673 '''
635 674 if not self.quiet:
636 675 opts['label'] = opts.get('label', '') + ' ui.status'
637 676 self.write(*msg, **opts)
638 677 def warn(self, *msg, **opts):
639 678 '''write warning message to output (stderr)
640 679
641 680 This adds an output label of "ui.warning".
642 681 '''
643 682 opts['label'] = opts.get('label', '') + ' ui.warning'
644 683 self.write_err(*msg, **opts)
645 684 def note(self, *msg, **opts):
646 685 '''write note to output (if ui.verbose is True)
647 686
648 687 This adds an output label of "ui.note".
649 688 '''
650 689 if self.verbose:
651 690 opts['label'] = opts.get('label', '') + ' ui.note'
652 691 self.write(*msg, **opts)
653 692 def debug(self, *msg, **opts):
654 693 '''write debug message to output (if ui.debugflag is True)
655 694
656 695 This adds an output label of "ui.debug".
657 696 '''
658 697 if self.debugflag:
659 698 opts['label'] = opts.get('label', '') + ' ui.debug'
660 699 self.write(*msg, **opts)
661 700 def edit(self, text, user):
662 701 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
663 702 text=True)
664 703 try:
665 704 f = os.fdopen(fd, "w")
666 705 f.write(text)
667 706 f.close()
668 707
669 708 editor = self.geteditor()
670 709
671 710 util.system("%s \"%s\"" % (editor, name),
672 711 environ={'HGUSER': user},
673 712 onerr=util.Abort, errprefix=_("edit failed"),
674 713 out=self.fout)
675 714
676 715 f = open(name)
677 716 t = f.read()
678 717 f.close()
679 718 finally:
680 719 os.unlink(name)
681 720
682 721 return t
683 722
684 723 def traceback(self, exc=None, force=False):
685 724 '''print exception traceback if traceback printing enabled or forced.
686 725 only to call in exception handler. returns true if traceback
687 726 printed.'''
688 727 if self.tracebackflag or force:
689 728 if exc is None:
690 729 exc = sys.exc_info()
691 730 cause = getattr(exc[1], 'cause', None)
692 731
693 732 if cause is not None:
694 733 causetb = traceback.format_tb(cause[2])
695 734 exctb = traceback.format_tb(exc[2])
696 735 exconly = traceback.format_exception_only(cause[0], cause[1])
697 736
698 737 # exclude frame where 'exc' was chained and rethrown from exctb
699 738 self.write_err('Traceback (most recent call last):\n',
700 739 ''.join(exctb[:-1]),
701 740 ''.join(causetb),
702 741 ''.join(exconly))
703 742 else:
704 743 traceback.print_exception(exc[0], exc[1], exc[2],
705 744 file=self.ferr)
706 745 return self.tracebackflag or force
707 746
708 747 def geteditor(self):
709 748 '''return editor to use'''
710 749 if sys.platform == 'plan9':
711 750 # vi is the MIPS instruction simulator on Plan 9. We
712 751 # instead default to E to plumb commit messages to
713 752 # avoid confusion.
714 753 editor = 'E'
715 754 else:
716 755 editor = 'vi'
717 756 return (os.environ.get("HGEDITOR") or
718 757 self.config("ui", "editor") or
719 758 os.environ.get("VISUAL") or
720 759 os.environ.get("EDITOR", editor))
721 760
722 761 def progress(self, topic, pos, item="", unit="", total=None):
723 762 '''show a progress message
724 763
725 764 With stock hg, this is simply a debug message that is hidden
726 765 by default, but with extensions or GUI tools it may be
727 766 visible. 'topic' is the current operation, 'item' is a
728 767 non-numeric marker of the current position (i.e. the currently
729 768 in-process file), 'pos' is the current numeric position (i.e.
730 769 revision, bytes, etc.), unit is a corresponding unit label,
731 770 and total is the highest expected pos.
732 771
733 772 Multiple nested topics may be active at a time.
734 773
735 774 All topics should be marked closed by setting pos to None at
736 775 termination.
737 776 '''
738 777
739 778 if pos is None or not self.debugflag:
740 779 return
741 780
742 781 if unit:
743 782 unit = ' ' + unit
744 783 if item:
745 784 item = ' ' + item
746 785
747 786 if total:
748 787 pct = 100.0 * pos / total
749 788 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
750 789 % (topic, item, pos, total, unit, pct))
751 790 else:
752 791 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
753 792
754 793 def log(self, service, *msg, **opts):
755 794 '''hook for logging facility extensions
756 795
757 796 service should be a readily-identifiable subsystem, which will
758 797 allow filtering.
759 798 message should be a newline-terminated string to log.
760 799 '''
761 800 pass
762 801
763 802 def label(self, msg, label):
764 803 '''style msg based on supplied label
765 804
766 805 Like ui.write(), this just returns msg unchanged, but extensions
767 806 and GUI tools can override it to allow styling output without
768 807 writing it.
769 808
770 809 ui.write(s, 'label') is equivalent to
771 810 ui.write(ui.label(s, 'label')).
772 811 '''
773 812 return msg
General Comments 0
You need to be logged in to leave comments. Login now