##// END OF EJS Templates
i18n: show the non-ASCII password prompt text correctly...
FUJIWARA Katsunori -
r19085:be207d9b stable
parent child Browse files
Show More
@@ -1,812 +1,813 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, 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 265 def configbytes(self, section, name, default=0, untrusted=False):
266 266 """parse a configuration element as a quantity in bytes
267 267
268 268 Units can be specified as b (bytes), k or kb (kilobytes), m or
269 269 mb (megabytes), g or gb (gigabytes).
270 270
271 271 >>> u = ui(); s = 'foo'
272 272 >>> u.setconfig(s, 'val1', '42')
273 273 >>> u.configbytes(s, 'val1')
274 274 42
275 275 >>> u.setconfig(s, 'val2', '42.5 kb')
276 276 >>> u.configbytes(s, 'val2')
277 277 43520
278 278 >>> u.configbytes(s, 'unknown', '7 MB')
279 279 7340032
280 280 >>> u.setconfig(s, 'invalid', 'somevalue')
281 281 >>> u.configbytes(s, 'invalid')
282 282 Traceback (most recent call last):
283 283 ...
284 284 ConfigError: foo.invalid is not a byte quantity ('somevalue')
285 285 """
286 286
287 287 orig = string = self.config(section, name)
288 288 if orig is None:
289 289 if not isinstance(default, str):
290 290 return default
291 291 orig = string = default
292 292 multiple = 1
293 293 m = re.match(r'([^kmbg]+?)\s*([kmg]?)b?$', string, re.I)
294 294 if m:
295 295 string, key = m.groups()
296 296 key = key.lower()
297 297 multiple = dict(k=1024, m=1048576, g=1073741824).get(key, 1)
298 298 try:
299 299 return int(float(string) * multiple)
300 300 except ValueError:
301 301 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
302 302 % (section, name, orig))
303 303
304 304 def configlist(self, section, name, default=None, untrusted=False):
305 305 """parse a configuration element as a list of comma/space separated
306 306 strings
307 307
308 308 >>> u = ui(); s = 'foo'
309 309 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
310 310 >>> u.configlist(s, 'list1')
311 311 ['this', 'is', 'a small', 'test']
312 312 """
313 313
314 314 def _parse_plain(parts, s, offset):
315 315 whitespace = False
316 316 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
317 317 whitespace = True
318 318 offset += 1
319 319 if offset >= len(s):
320 320 return None, parts, offset
321 321 if whitespace:
322 322 parts.append('')
323 323 if s[offset] == '"' and not parts[-1]:
324 324 return _parse_quote, parts, offset + 1
325 325 elif s[offset] == '"' and parts[-1][-1] == '\\':
326 326 parts[-1] = parts[-1][:-1] + s[offset]
327 327 return _parse_plain, parts, offset + 1
328 328 parts[-1] += s[offset]
329 329 return _parse_plain, parts, offset + 1
330 330
331 331 def _parse_quote(parts, s, offset):
332 332 if offset < len(s) and s[offset] == '"': # ""
333 333 parts.append('')
334 334 offset += 1
335 335 while offset < len(s) and (s[offset].isspace() or
336 336 s[offset] == ','):
337 337 offset += 1
338 338 return _parse_plain, parts, offset
339 339
340 340 while offset < len(s) and s[offset] != '"':
341 341 if (s[offset] == '\\' and offset + 1 < len(s)
342 342 and s[offset + 1] == '"'):
343 343 offset += 1
344 344 parts[-1] += '"'
345 345 else:
346 346 parts[-1] += s[offset]
347 347 offset += 1
348 348
349 349 if offset >= len(s):
350 350 real_parts = _configlist(parts[-1])
351 351 if not real_parts:
352 352 parts[-1] = '"'
353 353 else:
354 354 real_parts[0] = '"' + real_parts[0]
355 355 parts = parts[:-1]
356 356 parts.extend(real_parts)
357 357 return None, parts, offset
358 358
359 359 offset += 1
360 360 while offset < len(s) and s[offset] in [' ', ',']:
361 361 offset += 1
362 362
363 363 if offset < len(s):
364 364 if offset + 1 == len(s) and s[offset] == '"':
365 365 parts[-1] += '"'
366 366 offset += 1
367 367 else:
368 368 parts.append('')
369 369 else:
370 370 return None, parts, offset
371 371
372 372 return _parse_plain, parts, offset
373 373
374 374 def _configlist(s):
375 375 s = s.rstrip(' ,')
376 376 if not s:
377 377 return []
378 378 parser, parts, offset = _parse_plain, [''], 0
379 379 while parser:
380 380 parser, parts, offset = parser(parts, s, offset)
381 381 return parts
382 382
383 383 result = self.config(section, name, untrusted=untrusted)
384 384 if result is None:
385 385 result = default or []
386 386 if isinstance(result, basestring):
387 387 result = _configlist(result.lstrip(' ,\n'))
388 388 if result is None:
389 389 result = default or []
390 390 return result
391 391
392 392 def has_section(self, section, untrusted=False):
393 393 '''tell whether section exists in config.'''
394 394 return section in self._data(untrusted)
395 395
396 396 def configitems(self, section, untrusted=False):
397 397 items = self._data(untrusted).items(section)
398 398 if self.debugflag and not untrusted and self._reportuntrusted:
399 399 for k, v in self._ucfg.items(section):
400 400 if self._tcfg.get(section, k) != v:
401 401 self.debug("ignoring untrusted configuration option "
402 402 "%s.%s = %s\n" % (section, k, v))
403 403 return items
404 404
405 405 def walkconfig(self, untrusted=False):
406 406 cfg = self._data(untrusted)
407 407 for section in cfg.sections():
408 408 for name, value in self.configitems(section, untrusted):
409 409 yield section, name, value
410 410
411 411 def plain(self, feature=None):
412 412 '''is plain mode active?
413 413
414 414 Plain mode means that all configuration variables which affect
415 415 the behavior and output of Mercurial should be
416 416 ignored. Additionally, the output should be stable,
417 417 reproducible and suitable for use in scripts or applications.
418 418
419 419 The only way to trigger plain mode is by setting either the
420 420 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
421 421
422 422 The return value can either be
423 423 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
424 424 - True otherwise
425 425 '''
426 426 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
427 427 return False
428 428 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
429 429 if feature and exceptions:
430 430 return feature not in exceptions
431 431 return True
432 432
433 433 def username(self):
434 434 """Return default username to be used in commits.
435 435
436 436 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
437 437 and stop searching if one of these is set.
438 438 If not found and ui.askusername is True, ask the user, else use
439 439 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
440 440 """
441 441 user = os.environ.get("HGUSER")
442 442 if user is None:
443 443 user = self.config("ui", "username")
444 444 if user is not None:
445 445 user = os.path.expandvars(user)
446 446 if user is None:
447 447 user = os.environ.get("EMAIL")
448 448 if user is None and self.configbool("ui", "askusername"):
449 449 user = self.prompt(_("enter a commit username:"), default=None)
450 450 if user is None and not self.interactive():
451 451 try:
452 452 user = '%s@%s' % (util.getuser(), socket.getfqdn())
453 453 self.warn(_("no username found, using '%s' instead\n") % user)
454 454 except KeyError:
455 455 pass
456 456 if not user:
457 457 raise util.Abort(_('no username supplied (see "hg help config")'))
458 458 if "\n" in user:
459 459 raise util.Abort(_("username %s contains a newline\n") % repr(user))
460 460 return user
461 461
462 462 def shortuser(self, user):
463 463 """Return a short representation of a user name or email address."""
464 464 if not self.verbose:
465 465 user = util.shortuser(user)
466 466 return user
467 467
468 468 def expandpath(self, loc, default=None):
469 469 """Return repository location relative to cwd or from [paths]"""
470 470 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
471 471 return loc
472 472
473 473 path = self.config('paths', loc)
474 474 if not path and default is not None:
475 475 path = self.config('paths', default)
476 476 return path or loc
477 477
478 478 def pushbuffer(self):
479 479 self._buffers.append([])
480 480
481 481 def popbuffer(self, labeled=False):
482 482 '''pop the last buffer and return the buffered output
483 483
484 484 If labeled is True, any labels associated with buffered
485 485 output will be handled. By default, this has no effect
486 486 on the output returned, but extensions and GUI tools may
487 487 handle this argument and returned styled output. If output
488 488 is being buffered so it can be captured and parsed or
489 489 processed, labeled should not be set to True.
490 490 '''
491 491 return "".join(self._buffers.pop())
492 492
493 493 def write(self, *args, **opts):
494 494 '''write args to output
495 495
496 496 By default, this method simply writes to the buffer or stdout,
497 497 but extensions or GUI tools may override this method,
498 498 write_err(), popbuffer(), and label() to style output from
499 499 various parts of hg.
500 500
501 501 An optional keyword argument, "label", can be passed in.
502 502 This should be a string containing label names separated by
503 503 space. Label names take the form of "topic.type". For example,
504 504 ui.debug() issues a label of "ui.debug".
505 505
506 506 When labeling output for a specific command, a label of
507 507 "cmdname.type" is recommended. For example, status issues
508 508 a label of "status.modified" for modified files.
509 509 '''
510 510 if self._buffers:
511 511 self._buffers[-1].extend([str(a) for a in args])
512 512 else:
513 513 for a in args:
514 514 self.fout.write(str(a))
515 515
516 516 def write_err(self, *args, **opts):
517 517 try:
518 518 if not getattr(self.fout, 'closed', False):
519 519 self.fout.flush()
520 520 for a in args:
521 521 self.ferr.write(str(a))
522 522 # stderr may be buffered under win32 when redirected to files,
523 523 # including stdout.
524 524 if not getattr(self.ferr, 'closed', False):
525 525 self.ferr.flush()
526 526 except IOError, inst:
527 527 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
528 528 raise
529 529
530 530 def flush(self):
531 531 try: self.fout.flush()
532 532 except (IOError, ValueError): pass
533 533 try: self.ferr.flush()
534 534 except (IOError, ValueError): pass
535 535
536 536 def _isatty(self, fh):
537 537 if self.configbool('ui', 'nontty', False):
538 538 return False
539 539 return util.isatty(fh)
540 540
541 541 def interactive(self):
542 542 '''is interactive input allowed?
543 543
544 544 An interactive session is a session where input can be reasonably read
545 545 from `sys.stdin'. If this function returns false, any attempt to read
546 546 from stdin should fail with an error, unless a sensible default has been
547 547 specified.
548 548
549 549 Interactiveness is triggered by the value of the `ui.interactive'
550 550 configuration variable or - if it is unset - when `sys.stdin' points
551 551 to a terminal device.
552 552
553 553 This function refers to input only; for output, see `ui.formatted()'.
554 554 '''
555 555 i = self.configbool("ui", "interactive", None)
556 556 if i is None:
557 557 # some environments replace stdin without implementing isatty
558 558 # usually those are non-interactive
559 559 return self._isatty(self.fin)
560 560
561 561 return i
562 562
563 563 def termwidth(self):
564 564 '''how wide is the terminal in columns?
565 565 '''
566 566 if 'COLUMNS' in os.environ:
567 567 try:
568 568 return int(os.environ['COLUMNS'])
569 569 except ValueError:
570 570 pass
571 571 return util.termwidth()
572 572
573 573 def formatted(self):
574 574 '''should formatted output be used?
575 575
576 576 It is often desirable to format the output to suite the output medium.
577 577 Examples of this are truncating long lines or colorizing messages.
578 578 However, this is not often not desirable when piping output into other
579 579 utilities, e.g. `grep'.
580 580
581 581 Formatted output is triggered by the value of the `ui.formatted'
582 582 configuration variable or - if it is unset - when `sys.stdout' points
583 583 to a terminal device. Please note that `ui.formatted' should be
584 584 considered an implementation detail; it is not intended for use outside
585 585 Mercurial or its extensions.
586 586
587 587 This function refers to output only; for input, see `ui.interactive()'.
588 588 This function always returns false when in plain mode, see `ui.plain()'.
589 589 '''
590 590 if self.plain():
591 591 return False
592 592
593 593 i = self.configbool("ui", "formatted", None)
594 594 if i is None:
595 595 # some environments replace stdout without implementing isatty
596 596 # usually those are non-interactive
597 597 return self._isatty(self.fout)
598 598
599 599 return i
600 600
601 601 def _readline(self, prompt=''):
602 602 if self._isatty(self.fin):
603 603 try:
604 604 # magically add command line editing support, where
605 605 # available
606 606 import readline
607 607 # force demandimport to really load the module
608 608 readline.read_history_file
609 609 # windows sometimes raises something other than ImportError
610 610 except Exception:
611 611 pass
612 612
613 613 # call write() so output goes through subclassed implementation
614 614 # e.g. color extension on Windows
615 615 self.write(prompt)
616 616
617 617 # instead of trying to emulate raw_input, swap (self.fin,
618 618 # self.fout) with (sys.stdin, sys.stdout)
619 619 oldin = sys.stdin
620 620 oldout = sys.stdout
621 621 sys.stdin = self.fin
622 622 sys.stdout = self.fout
623 623 line = raw_input(' ')
624 624 sys.stdin = oldin
625 625 sys.stdout = oldout
626 626
627 627 # When stdin is in binary mode on Windows, it can cause
628 628 # raw_input() to emit an extra trailing carriage return
629 629 if os.linesep == '\r\n' and line and line[-1] == '\r':
630 630 line = line[:-1]
631 631 return line
632 632
633 633 def prompt(self, msg, default="y"):
634 634 """Prompt user with msg, read response.
635 635 If ui is not interactive, the default is returned.
636 636 """
637 637 if not self.interactive():
638 638 self.write(msg, ' ', default, "\n")
639 639 return default
640 640 try:
641 641 r = self._readline(self.label(msg, 'ui.prompt'))
642 642 if not r:
643 643 return default
644 644 return r
645 645 except EOFError:
646 646 raise util.Abort(_('response expected'))
647 647
648 648 def promptchoice(self, msg, choices, default=0):
649 649 """Prompt user with msg, read response, and ensure it matches
650 650 one of the provided choices. The index of the choice is returned.
651 651 choices is a sequence of acceptable responses with the format:
652 652 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
653 653 If ui is not interactive, the default is returned.
654 654 """
655 655 resps = [s[s.index('&') + 1].lower() for s in choices]
656 656 while True:
657 657 r = self.prompt(msg, resps[default])
658 658 if r.lower() in resps:
659 659 return resps.index(r.lower())
660 660 self.write(_("unrecognized response\n"))
661 661
662 662 def getpass(self, prompt=None, default=None):
663 663 if not self.interactive():
664 664 return default
665 665 try:
666 return getpass.getpass(prompt or _('password: '))
666 self.write(self.label(prompt or _('password: '), 'ui.prompt'))
667 return getpass.getpass('')
667 668 except EOFError:
668 669 raise util.Abort(_('response expected'))
669 670 def status(self, *msg, **opts):
670 671 '''write status message to output (if ui.quiet is False)
671 672
672 673 This adds an output label of "ui.status".
673 674 '''
674 675 if not self.quiet:
675 676 opts['label'] = opts.get('label', '') + ' ui.status'
676 677 self.write(*msg, **opts)
677 678 def warn(self, *msg, **opts):
678 679 '''write warning message to output (stderr)
679 680
680 681 This adds an output label of "ui.warning".
681 682 '''
682 683 opts['label'] = opts.get('label', '') + ' ui.warning'
683 684 self.write_err(*msg, **opts)
684 685 def note(self, *msg, **opts):
685 686 '''write note to output (if ui.verbose is True)
686 687
687 688 This adds an output label of "ui.note".
688 689 '''
689 690 if self.verbose:
690 691 opts['label'] = opts.get('label', '') + ' ui.note'
691 692 self.write(*msg, **opts)
692 693 def debug(self, *msg, **opts):
693 694 '''write debug message to output (if ui.debugflag is True)
694 695
695 696 This adds an output label of "ui.debug".
696 697 '''
697 698 if self.debugflag:
698 699 opts['label'] = opts.get('label', '') + ' ui.debug'
699 700 self.write(*msg, **opts)
700 701 def edit(self, text, user):
701 702 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
702 703 text=True)
703 704 try:
704 705 f = os.fdopen(fd, "w")
705 706 f.write(text)
706 707 f.close()
707 708
708 709 editor = self.geteditor()
709 710
710 711 util.system("%s \"%s\"" % (editor, name),
711 712 environ={'HGUSER': user},
712 713 onerr=util.Abort, errprefix=_("edit failed"),
713 714 out=self.fout)
714 715
715 716 f = open(name)
716 717 t = f.read()
717 718 f.close()
718 719 finally:
719 720 os.unlink(name)
720 721
721 722 return t
722 723
723 724 def traceback(self, exc=None, force=False):
724 725 '''print exception traceback if traceback printing enabled or forced.
725 726 only to call in exception handler. returns true if traceback
726 727 printed.'''
727 728 if self.tracebackflag or force:
728 729 if exc is None:
729 730 exc = sys.exc_info()
730 731 cause = getattr(exc[1], 'cause', None)
731 732
732 733 if cause is not None:
733 734 causetb = traceback.format_tb(cause[2])
734 735 exctb = traceback.format_tb(exc[2])
735 736 exconly = traceback.format_exception_only(cause[0], cause[1])
736 737
737 738 # exclude frame where 'exc' was chained and rethrown from exctb
738 739 self.write_err('Traceback (most recent call last):\n',
739 740 ''.join(exctb[:-1]),
740 741 ''.join(causetb),
741 742 ''.join(exconly))
742 743 else:
743 744 traceback.print_exception(exc[0], exc[1], exc[2],
744 745 file=self.ferr)
745 746 return self.tracebackflag or force
746 747
747 748 def geteditor(self):
748 749 '''return editor to use'''
749 750 if sys.platform == 'plan9':
750 751 # vi is the MIPS instruction simulator on Plan 9. We
751 752 # instead default to E to plumb commit messages to
752 753 # avoid confusion.
753 754 editor = 'E'
754 755 else:
755 756 editor = 'vi'
756 757 return (os.environ.get("HGEDITOR") or
757 758 self.config("ui", "editor") or
758 759 os.environ.get("VISUAL") or
759 760 os.environ.get("EDITOR", editor))
760 761
761 762 def progress(self, topic, pos, item="", unit="", total=None):
762 763 '''show a progress message
763 764
764 765 With stock hg, this is simply a debug message that is hidden
765 766 by default, but with extensions or GUI tools it may be
766 767 visible. 'topic' is the current operation, 'item' is a
767 768 non-numeric marker of the current position (i.e. the currently
768 769 in-process file), 'pos' is the current numeric position (i.e.
769 770 revision, bytes, etc.), unit is a corresponding unit label,
770 771 and total is the highest expected pos.
771 772
772 773 Multiple nested topics may be active at a time.
773 774
774 775 All topics should be marked closed by setting pos to None at
775 776 termination.
776 777 '''
777 778
778 779 if pos is None or not self.debugflag:
779 780 return
780 781
781 782 if unit:
782 783 unit = ' ' + unit
783 784 if item:
784 785 item = ' ' + item
785 786
786 787 if total:
787 788 pct = 100.0 * pos / total
788 789 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
789 790 % (topic, item, pos, total, unit, pct))
790 791 else:
791 792 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
792 793
793 794 def log(self, service, *msg, **opts):
794 795 '''hook for logging facility extensions
795 796
796 797 service should be a readily-identifiable subsystem, which will
797 798 allow filtering.
798 799 message should be a newline-terminated string to log.
799 800 '''
800 801 pass
801 802
802 803 def label(self, msg, label):
803 804 '''style msg based on supplied label
804 805
805 806 Like ui.write(), this just returns msg unchanged, but extensions
806 807 and GUI tools can override it to allow styling output without
807 808 writing it.
808 809
809 810 ui.write(s, 'label') is equivalent to
810 811 ui.write(ui.label(s, 'label')).
811 812 '''
812 813 return msg
General Comments 0
You need to be logged in to leave comments. Login now