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