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