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