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