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