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