##// END OF EJS Templates
ui: use ui out descriptor when calling util.system
Idan Kamara -
r14738:bcc1a9fd stable
parent child Browse files
Show More
@@ -1,711 +1,712 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 # instead of trying to emulate raw_input, swap our in/out
541 541 # with sys.stdin/out
542 542 old = sys.stdout, sys.stdin
543 543 sys.stdout, sys.stdin = self.fout, self.fin
544 544 line = raw_input(prompt)
545 545 sys.stdout, sys.stdin = old
546 546
547 547 # When stdin is in binary mode on Windows, it can cause
548 548 # raw_input() to emit an extra trailing carriage return
549 549 if os.linesep == '\r\n' and line and line[-1] == '\r':
550 550 line = line[:-1]
551 551 return line
552 552
553 553 def prompt(self, msg, default="y"):
554 554 """Prompt user with msg, read response.
555 555 If ui is not interactive, the default is returned.
556 556 """
557 557 if not self.interactive():
558 558 self.write(msg, ' ', default, "\n")
559 559 return default
560 560 try:
561 561 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
562 562 if not r:
563 563 return default
564 564 return r
565 565 except EOFError:
566 566 raise util.Abort(_('response expected'))
567 567
568 568 def promptchoice(self, msg, choices, default=0):
569 569 """Prompt user with msg, read response, and ensure it matches
570 570 one of the provided choices. The index of the choice is returned.
571 571 choices is a sequence of acceptable responses with the format:
572 572 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
573 573 If ui is not interactive, the default is returned.
574 574 """
575 575 resps = [s[s.index('&')+1].lower() for s in choices]
576 576 while True:
577 577 r = self.prompt(msg, resps[default])
578 578 if r.lower() in resps:
579 579 return resps.index(r.lower())
580 580 self.write(_("unrecognized response\n"))
581 581
582 582 def getpass(self, prompt=None, default=None):
583 583 if not self.interactive():
584 584 return default
585 585 try:
586 586 return getpass.getpass(prompt or _('password: '))
587 587 except EOFError:
588 588 raise util.Abort(_('response expected'))
589 589 def status(self, *msg, **opts):
590 590 '''write status message to output (if ui.quiet is False)
591 591
592 592 This adds an output label of "ui.status".
593 593 '''
594 594 if not self.quiet:
595 595 opts['label'] = opts.get('label', '') + ' ui.status'
596 596 self.write(*msg, **opts)
597 597 def warn(self, *msg, **opts):
598 598 '''write warning message to output (stderr)
599 599
600 600 This adds an output label of "ui.warning".
601 601 '''
602 602 opts['label'] = opts.get('label', '') + ' ui.warning'
603 603 self.write_err(*msg, **opts)
604 604 def note(self, *msg, **opts):
605 605 '''write note to output (if ui.verbose is True)
606 606
607 607 This adds an output label of "ui.note".
608 608 '''
609 609 if self.verbose:
610 610 opts['label'] = opts.get('label', '') + ' ui.note'
611 611 self.write(*msg, **opts)
612 612 def debug(self, *msg, **opts):
613 613 '''write debug message to output (if ui.debugflag is True)
614 614
615 615 This adds an output label of "ui.debug".
616 616 '''
617 617 if self.debugflag:
618 618 opts['label'] = opts.get('label', '') + ' ui.debug'
619 619 self.write(*msg, **opts)
620 620 def edit(self, text, user):
621 621 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
622 622 text=True)
623 623 try:
624 624 f = os.fdopen(fd, "w")
625 625 f.write(text)
626 626 f.close()
627 627
628 628 editor = self.geteditor()
629 629
630 630 util.system("%s \"%s\"" % (editor, name),
631 631 environ={'HGUSER': user},
632 onerr=util.Abort, errprefix=_("edit failed"))
632 onerr=util.Abort, errprefix=_("edit failed"),
633 out=self.fout)
633 634
634 635 f = open(name)
635 636 t = f.read()
636 637 f.close()
637 638 finally:
638 639 os.unlink(name)
639 640
640 641 return t
641 642
642 643 def traceback(self, exc=None):
643 644 '''print exception traceback if traceback printing enabled.
644 645 only to call in exception handler. returns true if traceback
645 646 printed.'''
646 647 if self.tracebackflag:
647 648 if exc:
648 649 traceback.print_exception(exc[0], exc[1], exc[2])
649 650 else:
650 651 traceback.print_exc()
651 652 return self.tracebackflag
652 653
653 654 def geteditor(self):
654 655 '''return editor to use'''
655 656 return (os.environ.get("HGEDITOR") or
656 657 self.config("ui", "editor") or
657 658 os.environ.get("VISUAL") or
658 659 os.environ.get("EDITOR", "vi"))
659 660
660 661 def progress(self, topic, pos, item="", unit="", total=None):
661 662 '''show a progress message
662 663
663 664 With stock hg, this is simply a debug message that is hidden
664 665 by default, but with extensions or GUI tools it may be
665 666 visible. 'topic' is the current operation, 'item' is a
666 667 non-numeric marker of the current position (ie the currently
667 668 in-process file), 'pos' is the current numeric position (ie
668 669 revision, bytes, etc.), unit is a corresponding unit label,
669 670 and total is the highest expected pos.
670 671
671 672 Multiple nested topics may be active at a time.
672 673
673 674 All topics should be marked closed by setting pos to None at
674 675 termination.
675 676 '''
676 677
677 678 if pos is None or not self.debugflag:
678 679 return
679 680
680 681 if unit:
681 682 unit = ' ' + unit
682 683 if item:
683 684 item = ' ' + item
684 685
685 686 if total:
686 687 pct = 100.0 * pos / total
687 688 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
688 689 % (topic, item, pos, total, unit, pct))
689 690 else:
690 691 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
691 692
692 693 def log(self, service, message):
693 694 '''hook for logging facility extensions
694 695
695 696 service should be a readily-identifiable subsystem, which will
696 697 allow filtering.
697 698 message should be a newline-terminated string to log.
698 699 '''
699 700 pass
700 701
701 702 def label(self, msg, label):
702 703 '''style msg based on supplied label
703 704
704 705 Like ui.write(), this just returns msg unchanged, but extensions
705 706 and GUI tools can override it to allow styling output without
706 707 writing it.
707 708
708 709 ui.write(s, 'label') is equivalent to
709 710 ui.write(ui.label(s, 'label')).
710 711 '''
711 712 return msg
General Comments 0
You need to be logged in to leave comments. Login now