##// END OF EJS Templates
config: give a more detailed sample repo config...
Jordi Gutiérrez Hermoso -
r22836:5a831e4e default
parent child Browse files
Show More
@@ -1,894 +1,906
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, formatter
11 11 from node import hex
12 12
13 13 samplehgrcs = {
14 14 'user':
15 15 """# example user config (see "hg help config" for more info)
16 16 [ui]
17 17 # name and email, e.g.
18 18 # username = Jane Doe <jdoe@example.com>
19 19 username =
20 20
21 21 [extensions]
22 22 # uncomment these lines to enable some popular extensions
23 23 # (see "hg help extensions" for more info)
24 24 #
25 25 # pager =
26 26 # progress =
27 27 # color =""",
28 28
29 29 'local':
30 30 """# example repository config (see "hg help config" for more info)
31 [paths]
32 # path aliases to other clones of this repo in URLs or filesystem paths
33 # (see "hg help config.paths" for more info)
34 #
35 # default = http://example.com/hg/example-repo
36 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
37 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
38 # my-clone = /home/jdoe/jdoes-clone
39
40 [ui]
41 # name and email (local to this repository, optional), e.g.
42 # username = Jane Doe <jdoe@example.com>
31 43 """,
32 44
33 45 'global':
34 46 """# example system-wide hg config (see "hg help config" for more info)
35 47
36 48 [extensions]
37 49 # uncomment these lines to enable some popular extensions
38 50 # (see "hg help extensions" for more info)
39 51 #
40 52 # blackbox =
41 53 # progress =
42 54 # color =
43 55 # pager =""",
44 56 }
45 57
46 58 class ui(object):
47 59 def __init__(self, src=None):
48 60 # _buffers: used for temporary capture of output
49 61 self._buffers = []
50 62 # _bufferstates: Should the temporary capture includes stderr
51 63 self._bufferstates = []
52 64 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
53 65 self._reportuntrusted = True
54 66 self._ocfg = config.config() # overlay
55 67 self._tcfg = config.config() # trusted
56 68 self._ucfg = config.config() # untrusted
57 69 self._trustusers = set()
58 70 self._trustgroups = set()
59 71 self.callhooks = True
60 72
61 73 if src:
62 74 self.fout = src.fout
63 75 self.ferr = src.ferr
64 76 self.fin = src.fin
65 77
66 78 self._tcfg = src._tcfg.copy()
67 79 self._ucfg = src._ucfg.copy()
68 80 self._ocfg = src._ocfg.copy()
69 81 self._trustusers = src._trustusers.copy()
70 82 self._trustgroups = src._trustgroups.copy()
71 83 self.environ = src.environ
72 84 self.callhooks = src.callhooks
73 85 self.fixconfig()
74 86 else:
75 87 self.fout = sys.stdout
76 88 self.ferr = sys.stderr
77 89 self.fin = sys.stdin
78 90
79 91 # shared read-only environment
80 92 self.environ = os.environ
81 93 # we always trust global config files
82 94 for f in scmutil.rcpath():
83 95 self.readconfig(f, trust=True)
84 96
85 97 def copy(self):
86 98 return self.__class__(self)
87 99
88 100 def formatter(self, topic, opts):
89 101 return formatter.formatter(self, topic, opts)
90 102
91 103 def _trusted(self, fp, f):
92 104 st = util.fstat(fp)
93 105 if util.isowner(st):
94 106 return True
95 107
96 108 tusers, tgroups = self._trustusers, self._trustgroups
97 109 if '*' in tusers or '*' in tgroups:
98 110 return True
99 111
100 112 user = util.username(st.st_uid)
101 113 group = util.groupname(st.st_gid)
102 114 if user in tusers or group in tgroups or user == util.username():
103 115 return True
104 116
105 117 if self._reportuntrusted:
106 118 self.warn(_('not trusting file %s from untrusted '
107 119 'user %s, group %s\n') % (f, user, group))
108 120 return False
109 121
110 122 def readconfig(self, filename, root=None, trust=False,
111 123 sections=None, remap=None):
112 124 try:
113 125 fp = open(filename)
114 126 except IOError:
115 127 if not sections: # ignore unless we were looking for something
116 128 return
117 129 raise
118 130
119 131 cfg = config.config()
120 132 trusted = sections or trust or self._trusted(fp, filename)
121 133
122 134 try:
123 135 cfg.read(filename, fp, sections=sections, remap=remap)
124 136 fp.close()
125 137 except error.ConfigError, inst:
126 138 if trusted:
127 139 raise
128 140 self.warn(_("ignored: %s\n") % str(inst))
129 141
130 142 if self.plain():
131 143 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
132 144 'logtemplate', 'style',
133 145 'traceback', 'verbose'):
134 146 if k in cfg['ui']:
135 147 del cfg['ui'][k]
136 148 for k, v in cfg.items('defaults'):
137 149 del cfg['defaults'][k]
138 150 # Don't remove aliases from the configuration if in the exceptionlist
139 151 if self.plain('alias'):
140 152 for k, v in cfg.items('alias'):
141 153 del cfg['alias'][k]
142 154
143 155 if trusted:
144 156 self._tcfg.update(cfg)
145 157 self._tcfg.update(self._ocfg)
146 158 self._ucfg.update(cfg)
147 159 self._ucfg.update(self._ocfg)
148 160
149 161 if root is None:
150 162 root = os.path.expanduser('~')
151 163 self.fixconfig(root=root)
152 164
153 165 def fixconfig(self, root=None, section=None):
154 166 if section in (None, 'paths'):
155 167 # expand vars and ~
156 168 # translate paths relative to root (or home) into absolute paths
157 169 root = root or os.getcwd()
158 170 for c in self._tcfg, self._ucfg, self._ocfg:
159 171 for n, p in c.items('paths'):
160 172 if not p:
161 173 continue
162 174 if '%%' in p:
163 175 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
164 176 % (n, p, self.configsource('paths', n)))
165 177 p = p.replace('%%', '%')
166 178 p = util.expandpath(p)
167 179 if not util.hasscheme(p) and not os.path.isabs(p):
168 180 p = os.path.normpath(os.path.join(root, p))
169 181 c.set("paths", n, p)
170 182
171 183 if section in (None, 'ui'):
172 184 # update ui options
173 185 self.debugflag = self.configbool('ui', 'debug')
174 186 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
175 187 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
176 188 if self.verbose and self.quiet:
177 189 self.quiet = self.verbose = False
178 190 self._reportuntrusted = self.debugflag or self.configbool("ui",
179 191 "report_untrusted", True)
180 192 self.tracebackflag = self.configbool('ui', 'traceback', False)
181 193
182 194 if section in (None, 'trusted'):
183 195 # update trust information
184 196 self._trustusers.update(self.configlist('trusted', 'users'))
185 197 self._trustgroups.update(self.configlist('trusted', 'groups'))
186 198
187 199 def backupconfig(self, section, item):
188 200 return (self._ocfg.backup(section, item),
189 201 self._tcfg.backup(section, item),
190 202 self._ucfg.backup(section, item),)
191 203 def restoreconfig(self, data):
192 204 self._ocfg.restore(data[0])
193 205 self._tcfg.restore(data[1])
194 206 self._ucfg.restore(data[2])
195 207
196 208 def setconfig(self, section, name, value, source=''):
197 209 for cfg in (self._ocfg, self._tcfg, self._ucfg):
198 210 cfg.set(section, name, value, source)
199 211 self.fixconfig(section=section)
200 212
201 213 def _data(self, untrusted):
202 214 return untrusted and self._ucfg or self._tcfg
203 215
204 216 def configsource(self, section, name, untrusted=False):
205 217 return self._data(untrusted).source(section, name) or 'none'
206 218
207 219 def config(self, section, name, default=None, untrusted=False):
208 220 if isinstance(name, list):
209 221 alternates = name
210 222 else:
211 223 alternates = [name]
212 224
213 225 for n in alternates:
214 226 value = self._data(untrusted).get(section, n, None)
215 227 if value is not None:
216 228 name = n
217 229 break
218 230 else:
219 231 value = default
220 232
221 233 if self.debugflag and not untrusted and self._reportuntrusted:
222 234 for n in alternates:
223 235 uvalue = self._ucfg.get(section, n)
224 236 if uvalue is not None and uvalue != value:
225 237 self.debug("ignoring untrusted configuration option "
226 238 "%s.%s = %s\n" % (section, n, uvalue))
227 239 return value
228 240
229 241 def configpath(self, section, name, default=None, untrusted=False):
230 242 'get a path config item, expanded relative to repo root or config file'
231 243 v = self.config(section, name, default, untrusted)
232 244 if v is None:
233 245 return None
234 246 if not os.path.isabs(v) or "://" not in v:
235 247 src = self.configsource(section, name, untrusted)
236 248 if ':' in src:
237 249 base = os.path.dirname(src.rsplit(':')[0])
238 250 v = os.path.join(base, os.path.expanduser(v))
239 251 return v
240 252
241 253 def configbool(self, section, name, default=False, untrusted=False):
242 254 """parse a configuration element as a boolean
243 255
244 256 >>> u = ui(); s = 'foo'
245 257 >>> u.setconfig(s, 'true', 'yes')
246 258 >>> u.configbool(s, 'true')
247 259 True
248 260 >>> u.setconfig(s, 'false', 'no')
249 261 >>> u.configbool(s, 'false')
250 262 False
251 263 >>> u.configbool(s, 'unknown')
252 264 False
253 265 >>> u.configbool(s, 'unknown', True)
254 266 True
255 267 >>> u.setconfig(s, 'invalid', 'somevalue')
256 268 >>> u.configbool(s, 'invalid')
257 269 Traceback (most recent call last):
258 270 ...
259 271 ConfigError: foo.invalid is not a boolean ('somevalue')
260 272 """
261 273
262 274 v = self.config(section, name, None, untrusted)
263 275 if v is None:
264 276 return default
265 277 if isinstance(v, bool):
266 278 return v
267 279 b = util.parsebool(v)
268 280 if b is None:
269 281 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
270 282 % (section, name, v))
271 283 return b
272 284
273 285 def configint(self, section, name, default=None, untrusted=False):
274 286 """parse a configuration element as an integer
275 287
276 288 >>> u = ui(); s = 'foo'
277 289 >>> u.setconfig(s, 'int1', '42')
278 290 >>> u.configint(s, 'int1')
279 291 42
280 292 >>> u.setconfig(s, 'int2', '-42')
281 293 >>> u.configint(s, 'int2')
282 294 -42
283 295 >>> u.configint(s, 'unknown', 7)
284 296 7
285 297 >>> u.setconfig(s, 'invalid', 'somevalue')
286 298 >>> u.configint(s, 'invalid')
287 299 Traceback (most recent call last):
288 300 ...
289 301 ConfigError: foo.invalid is not an integer ('somevalue')
290 302 """
291 303
292 304 v = self.config(section, name, None, untrusted)
293 305 if v is None:
294 306 return default
295 307 try:
296 308 return int(v)
297 309 except ValueError:
298 310 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
299 311 % (section, name, v))
300 312
301 313 def configbytes(self, section, name, default=0, untrusted=False):
302 314 """parse a configuration element as a quantity in bytes
303 315
304 316 Units can be specified as b (bytes), k or kb (kilobytes), m or
305 317 mb (megabytes), g or gb (gigabytes).
306 318
307 319 >>> u = ui(); s = 'foo'
308 320 >>> u.setconfig(s, 'val1', '42')
309 321 >>> u.configbytes(s, 'val1')
310 322 42
311 323 >>> u.setconfig(s, 'val2', '42.5 kb')
312 324 >>> u.configbytes(s, 'val2')
313 325 43520
314 326 >>> u.configbytes(s, 'unknown', '7 MB')
315 327 7340032
316 328 >>> u.setconfig(s, 'invalid', 'somevalue')
317 329 >>> u.configbytes(s, 'invalid')
318 330 Traceback (most recent call last):
319 331 ...
320 332 ConfigError: foo.invalid is not a byte quantity ('somevalue')
321 333 """
322 334
323 335 value = self.config(section, name)
324 336 if value is None:
325 337 if not isinstance(default, str):
326 338 return default
327 339 value = default
328 340 try:
329 341 return util.sizetoint(value)
330 342 except error.ParseError:
331 343 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
332 344 % (section, name, value))
333 345
334 346 def configlist(self, section, name, default=None, untrusted=False):
335 347 """parse a configuration element as a list of comma/space separated
336 348 strings
337 349
338 350 >>> u = ui(); s = 'foo'
339 351 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
340 352 >>> u.configlist(s, 'list1')
341 353 ['this', 'is', 'a small', 'test']
342 354 """
343 355
344 356 def _parse_plain(parts, s, offset):
345 357 whitespace = False
346 358 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
347 359 whitespace = True
348 360 offset += 1
349 361 if offset >= len(s):
350 362 return None, parts, offset
351 363 if whitespace:
352 364 parts.append('')
353 365 if s[offset] == '"' and not parts[-1]:
354 366 return _parse_quote, parts, offset + 1
355 367 elif s[offset] == '"' and parts[-1][-1] == '\\':
356 368 parts[-1] = parts[-1][:-1] + s[offset]
357 369 return _parse_plain, parts, offset + 1
358 370 parts[-1] += s[offset]
359 371 return _parse_plain, parts, offset + 1
360 372
361 373 def _parse_quote(parts, s, offset):
362 374 if offset < len(s) and s[offset] == '"': # ""
363 375 parts.append('')
364 376 offset += 1
365 377 while offset < len(s) and (s[offset].isspace() or
366 378 s[offset] == ','):
367 379 offset += 1
368 380 return _parse_plain, parts, offset
369 381
370 382 while offset < len(s) and s[offset] != '"':
371 383 if (s[offset] == '\\' and offset + 1 < len(s)
372 384 and s[offset + 1] == '"'):
373 385 offset += 1
374 386 parts[-1] += '"'
375 387 else:
376 388 parts[-1] += s[offset]
377 389 offset += 1
378 390
379 391 if offset >= len(s):
380 392 real_parts = _configlist(parts[-1])
381 393 if not real_parts:
382 394 parts[-1] = '"'
383 395 else:
384 396 real_parts[0] = '"' + real_parts[0]
385 397 parts = parts[:-1]
386 398 parts.extend(real_parts)
387 399 return None, parts, offset
388 400
389 401 offset += 1
390 402 while offset < len(s) and s[offset] in [' ', ',']:
391 403 offset += 1
392 404
393 405 if offset < len(s):
394 406 if offset + 1 == len(s) and s[offset] == '"':
395 407 parts[-1] += '"'
396 408 offset += 1
397 409 else:
398 410 parts.append('')
399 411 else:
400 412 return None, parts, offset
401 413
402 414 return _parse_plain, parts, offset
403 415
404 416 def _configlist(s):
405 417 s = s.rstrip(' ,')
406 418 if not s:
407 419 return []
408 420 parser, parts, offset = _parse_plain, [''], 0
409 421 while parser:
410 422 parser, parts, offset = parser(parts, s, offset)
411 423 return parts
412 424
413 425 result = self.config(section, name, untrusted=untrusted)
414 426 if result is None:
415 427 result = default or []
416 428 if isinstance(result, basestring):
417 429 result = _configlist(result.lstrip(' ,\n'))
418 430 if result is None:
419 431 result = default or []
420 432 return result
421 433
422 434 def has_section(self, section, untrusted=False):
423 435 '''tell whether section exists in config.'''
424 436 return section in self._data(untrusted)
425 437
426 438 def configitems(self, section, untrusted=False):
427 439 items = self._data(untrusted).items(section)
428 440 if self.debugflag and not untrusted and self._reportuntrusted:
429 441 for k, v in self._ucfg.items(section):
430 442 if self._tcfg.get(section, k) != v:
431 443 self.debug("ignoring untrusted configuration option "
432 444 "%s.%s = %s\n" % (section, k, v))
433 445 return items
434 446
435 447 def walkconfig(self, untrusted=False):
436 448 cfg = self._data(untrusted)
437 449 for section in cfg.sections():
438 450 for name, value in self.configitems(section, untrusted):
439 451 yield section, name, value
440 452
441 453 def plain(self, feature=None):
442 454 '''is plain mode active?
443 455
444 456 Plain mode means that all configuration variables which affect
445 457 the behavior and output of Mercurial should be
446 458 ignored. Additionally, the output should be stable,
447 459 reproducible and suitable for use in scripts or applications.
448 460
449 461 The only way to trigger plain mode is by setting either the
450 462 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
451 463
452 464 The return value can either be
453 465 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
454 466 - True otherwise
455 467 '''
456 468 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
457 469 return False
458 470 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
459 471 if feature and exceptions:
460 472 return feature not in exceptions
461 473 return True
462 474
463 475 def username(self):
464 476 """Return default username to be used in commits.
465 477
466 478 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
467 479 and stop searching if one of these is set.
468 480 If not found and ui.askusername is True, ask the user, else use
469 481 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
470 482 """
471 483 user = os.environ.get("HGUSER")
472 484 if user is None:
473 485 user = self.config("ui", ["username", "user"])
474 486 if user is not None:
475 487 user = os.path.expandvars(user)
476 488 if user is None:
477 489 user = os.environ.get("EMAIL")
478 490 if user is None and self.configbool("ui", "askusername"):
479 491 user = self.prompt(_("enter a commit username:"), default=None)
480 492 if user is None and not self.interactive():
481 493 try:
482 494 user = '%s@%s' % (util.getuser(), socket.getfqdn())
483 495 self.warn(_("no username found, using '%s' instead\n") % user)
484 496 except KeyError:
485 497 pass
486 498 if not user:
487 499 raise util.Abort(_('no username supplied'),
488 500 hint=_('use "hg config --edit" '
489 501 'to set your username'))
490 502 if "\n" in user:
491 503 raise util.Abort(_("username %s contains a newline\n") % repr(user))
492 504 return user
493 505
494 506 def shortuser(self, user):
495 507 """Return a short representation of a user name or email address."""
496 508 if not self.verbose:
497 509 user = util.shortuser(user)
498 510 return user
499 511
500 512 def expandpath(self, loc, default=None):
501 513 """Return repository location relative to cwd or from [paths]"""
502 514 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
503 515 return loc
504 516
505 517 path = self.config('paths', loc)
506 518 if not path and default is not None:
507 519 path = self.config('paths', default)
508 520 return path or loc
509 521
510 522 def pushbuffer(self, error=False):
511 523 """install a buffer to capture standar output of the ui object
512 524
513 525 If error is True, the error output will be captured too."""
514 526 self._buffers.append([])
515 527 self._bufferstates.append(error)
516 528
517 529 def popbuffer(self, labeled=False):
518 530 '''pop the last buffer and return the buffered output
519 531
520 532 If labeled is True, any labels associated with buffered
521 533 output will be handled. By default, this has no effect
522 534 on the output returned, but extensions and GUI tools may
523 535 handle this argument and returned styled output. If output
524 536 is being buffered so it can be captured and parsed or
525 537 processed, labeled should not be set to True.
526 538 '''
527 539 self._bufferstates.pop()
528 540 return "".join(self._buffers.pop())
529 541
530 542 def write(self, *args, **opts):
531 543 '''write args to output
532 544
533 545 By default, this method simply writes to the buffer or stdout,
534 546 but extensions or GUI tools may override this method,
535 547 write_err(), popbuffer(), and label() to style output from
536 548 various parts of hg.
537 549
538 550 An optional keyword argument, "label", can be passed in.
539 551 This should be a string containing label names separated by
540 552 space. Label names take the form of "topic.type". For example,
541 553 ui.debug() issues a label of "ui.debug".
542 554
543 555 When labeling output for a specific command, a label of
544 556 "cmdname.type" is recommended. For example, status issues
545 557 a label of "status.modified" for modified files.
546 558 '''
547 559 if self._buffers:
548 560 self._buffers[-1].extend([str(a) for a in args])
549 561 else:
550 562 for a in args:
551 563 self.fout.write(str(a))
552 564
553 565 def write_err(self, *args, **opts):
554 566 try:
555 567 if self._bufferstates and self._bufferstates[-1]:
556 568 return self.write(*args, **opts)
557 569 if not getattr(self.fout, 'closed', False):
558 570 self.fout.flush()
559 571 for a in args:
560 572 self.ferr.write(str(a))
561 573 # stderr may be buffered under win32 when redirected to files,
562 574 # including stdout.
563 575 if not getattr(self.ferr, 'closed', False):
564 576 self.ferr.flush()
565 577 except IOError, inst:
566 578 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
567 579 raise
568 580
569 581 def flush(self):
570 582 try: self.fout.flush()
571 583 except (IOError, ValueError): pass
572 584 try: self.ferr.flush()
573 585 except (IOError, ValueError): pass
574 586
575 587 def _isatty(self, fh):
576 588 if self.configbool('ui', 'nontty', False):
577 589 return False
578 590 return util.isatty(fh)
579 591
580 592 def interactive(self):
581 593 '''is interactive input allowed?
582 594
583 595 An interactive session is a session where input can be reasonably read
584 596 from `sys.stdin'. If this function returns false, any attempt to read
585 597 from stdin should fail with an error, unless a sensible default has been
586 598 specified.
587 599
588 600 Interactiveness is triggered by the value of the `ui.interactive'
589 601 configuration variable or - if it is unset - when `sys.stdin' points
590 602 to a terminal device.
591 603
592 604 This function refers to input only; for output, see `ui.formatted()'.
593 605 '''
594 606 i = self.configbool("ui", "interactive", None)
595 607 if i is None:
596 608 # some environments replace stdin without implementing isatty
597 609 # usually those are non-interactive
598 610 return self._isatty(self.fin)
599 611
600 612 return i
601 613
602 614 def termwidth(self):
603 615 '''how wide is the terminal in columns?
604 616 '''
605 617 if 'COLUMNS' in os.environ:
606 618 try:
607 619 return int(os.environ['COLUMNS'])
608 620 except ValueError:
609 621 pass
610 622 return util.termwidth()
611 623
612 624 def formatted(self):
613 625 '''should formatted output be used?
614 626
615 627 It is often desirable to format the output to suite the output medium.
616 628 Examples of this are truncating long lines or colorizing messages.
617 629 However, this is not often not desirable when piping output into other
618 630 utilities, e.g. `grep'.
619 631
620 632 Formatted output is triggered by the value of the `ui.formatted'
621 633 configuration variable or - if it is unset - when `sys.stdout' points
622 634 to a terminal device. Please note that `ui.formatted' should be
623 635 considered an implementation detail; it is not intended for use outside
624 636 Mercurial or its extensions.
625 637
626 638 This function refers to output only; for input, see `ui.interactive()'.
627 639 This function always returns false when in plain mode, see `ui.plain()'.
628 640 '''
629 641 if self.plain():
630 642 return False
631 643
632 644 i = self.configbool("ui", "formatted", None)
633 645 if i is None:
634 646 # some environments replace stdout without implementing isatty
635 647 # usually those are non-interactive
636 648 return self._isatty(self.fout)
637 649
638 650 return i
639 651
640 652 def _readline(self, prompt=''):
641 653 if self._isatty(self.fin):
642 654 try:
643 655 # magically add command line editing support, where
644 656 # available
645 657 import readline
646 658 # force demandimport to really load the module
647 659 readline.read_history_file
648 660 # windows sometimes raises something other than ImportError
649 661 except Exception:
650 662 pass
651 663
652 664 # call write() so output goes through subclassed implementation
653 665 # e.g. color extension on Windows
654 666 self.write(prompt)
655 667
656 668 # instead of trying to emulate raw_input, swap (self.fin,
657 669 # self.fout) with (sys.stdin, sys.stdout)
658 670 oldin = sys.stdin
659 671 oldout = sys.stdout
660 672 sys.stdin = self.fin
661 673 sys.stdout = self.fout
662 674 # prompt ' ' must exist; otherwise readline may delete entire line
663 675 # - http://bugs.python.org/issue12833
664 676 line = raw_input(' ')
665 677 sys.stdin = oldin
666 678 sys.stdout = oldout
667 679
668 680 # When stdin is in binary mode on Windows, it can cause
669 681 # raw_input() to emit an extra trailing carriage return
670 682 if os.linesep == '\r\n' and line and line[-1] == '\r':
671 683 line = line[:-1]
672 684 return line
673 685
674 686 def prompt(self, msg, default="y"):
675 687 """Prompt user with msg, read response.
676 688 If ui is not interactive, the default is returned.
677 689 """
678 690 if not self.interactive():
679 691 self.write(msg, ' ', default, "\n")
680 692 return default
681 693 try:
682 694 r = self._readline(self.label(msg, 'ui.prompt'))
683 695 if not r:
684 696 r = default
685 697 # sometimes self.interactive disagrees with isatty,
686 698 # show response provided on stdin when simulating
687 699 # but commandserver
688 700 if (not util.isatty(self.fin)
689 701 and not self.configbool('ui', 'nontty')):
690 702 self.write(r, "\n")
691 703 return r
692 704 except EOFError:
693 705 raise util.Abort(_('response expected'))
694 706
695 707 @staticmethod
696 708 def extractchoices(prompt):
697 709 """Extract prompt message and list of choices from specified prompt.
698 710
699 711 This returns tuple "(message, choices)", and "choices" is the
700 712 list of tuple "(response character, text without &)".
701 713 """
702 714 parts = prompt.split('$$')
703 715 msg = parts[0].rstrip(' ')
704 716 choices = [p.strip(' ') for p in parts[1:]]
705 717 return (msg,
706 718 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
707 719 for s in choices])
708 720
709 721 def promptchoice(self, prompt, default=0):
710 722 """Prompt user with a message, read response, and ensure it matches
711 723 one of the provided choices. The prompt is formatted as follows:
712 724
713 725 "would you like fries with that (Yn)? $$ &Yes $$ &No"
714 726
715 727 The index of the choice is returned. Responses are case
716 728 insensitive. If ui is not interactive, the default is
717 729 returned.
718 730 """
719 731
720 732 msg, choices = self.extractchoices(prompt)
721 733 resps = [r for r, t in choices]
722 734 while True:
723 735 r = self.prompt(msg, resps[default])
724 736 if r.lower() in resps:
725 737 return resps.index(r.lower())
726 738 self.write(_("unrecognized response\n"))
727 739
728 740 def getpass(self, prompt=None, default=None):
729 741 if not self.interactive():
730 742 return default
731 743 try:
732 744 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
733 745 # disable getpass() only if explicitly specified. it's still valid
734 746 # to interact with tty even if fin is not a tty.
735 747 if self.configbool('ui', 'nontty'):
736 748 return self.fin.readline().rstrip('\n')
737 749 else:
738 750 return getpass.getpass('')
739 751 except EOFError:
740 752 raise util.Abort(_('response expected'))
741 753 def status(self, *msg, **opts):
742 754 '''write status message to output (if ui.quiet is False)
743 755
744 756 This adds an output label of "ui.status".
745 757 '''
746 758 if not self.quiet:
747 759 opts['label'] = opts.get('label', '') + ' ui.status'
748 760 self.write(*msg, **opts)
749 761 def warn(self, *msg, **opts):
750 762 '''write warning message to output (stderr)
751 763
752 764 This adds an output label of "ui.warning".
753 765 '''
754 766 opts['label'] = opts.get('label', '') + ' ui.warning'
755 767 self.write_err(*msg, **opts)
756 768 def note(self, *msg, **opts):
757 769 '''write note to output (if ui.verbose is True)
758 770
759 771 This adds an output label of "ui.note".
760 772 '''
761 773 if self.verbose:
762 774 opts['label'] = opts.get('label', '') + ' ui.note'
763 775 self.write(*msg, **opts)
764 776 def debug(self, *msg, **opts):
765 777 '''write debug message to output (if ui.debugflag is True)
766 778
767 779 This adds an output label of "ui.debug".
768 780 '''
769 781 if self.debugflag:
770 782 opts['label'] = opts.get('label', '') + ' ui.debug'
771 783 self.write(*msg, **opts)
772 784 def edit(self, text, user, extra={}, editform=None):
773 785 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
774 786 text=True)
775 787 try:
776 788 f = os.fdopen(fd, "w")
777 789 f.write(text)
778 790 f.close()
779 791
780 792 environ = {'HGUSER': user}
781 793 if 'transplant_source' in extra:
782 794 environ.update({'HGREVISION': hex(extra['transplant_source'])})
783 795 for label in ('source', 'rebase_source'):
784 796 if label in extra:
785 797 environ.update({'HGREVISION': extra[label]})
786 798 break
787 799 if editform:
788 800 environ.update({'HGEDITFORM': editform})
789 801
790 802 editor = self.geteditor()
791 803
792 804 util.system("%s \"%s\"" % (editor, name),
793 805 environ=environ,
794 806 onerr=util.Abort, errprefix=_("edit failed"),
795 807 out=self.fout)
796 808
797 809 f = open(name)
798 810 t = f.read()
799 811 f.close()
800 812 finally:
801 813 os.unlink(name)
802 814
803 815 return t
804 816
805 817 def traceback(self, exc=None, force=False):
806 818 '''print exception traceback if traceback printing enabled or forced.
807 819 only to call in exception handler. returns true if traceback
808 820 printed.'''
809 821 if self.tracebackflag or force:
810 822 if exc is None:
811 823 exc = sys.exc_info()
812 824 cause = getattr(exc[1], 'cause', None)
813 825
814 826 if cause is not None:
815 827 causetb = traceback.format_tb(cause[2])
816 828 exctb = traceback.format_tb(exc[2])
817 829 exconly = traceback.format_exception_only(cause[0], cause[1])
818 830
819 831 # exclude frame where 'exc' was chained and rethrown from exctb
820 832 self.write_err('Traceback (most recent call last):\n',
821 833 ''.join(exctb[:-1]),
822 834 ''.join(causetb),
823 835 ''.join(exconly))
824 836 else:
825 837 traceback.print_exception(exc[0], exc[1], exc[2],
826 838 file=self.ferr)
827 839 return self.tracebackflag or force
828 840
829 841 def geteditor(self):
830 842 '''return editor to use'''
831 843 if sys.platform == 'plan9':
832 844 # vi is the MIPS instruction simulator on Plan 9. We
833 845 # instead default to E to plumb commit messages to
834 846 # avoid confusion.
835 847 editor = 'E'
836 848 else:
837 849 editor = 'vi'
838 850 return (os.environ.get("HGEDITOR") or
839 851 self.config("ui", "editor") or
840 852 os.environ.get("VISUAL") or
841 853 os.environ.get("EDITOR", editor))
842 854
843 855 def progress(self, topic, pos, item="", unit="", total=None):
844 856 '''show a progress message
845 857
846 858 With stock hg, this is simply a debug message that is hidden
847 859 by default, but with extensions or GUI tools it may be
848 860 visible. 'topic' is the current operation, 'item' is a
849 861 non-numeric marker of the current position (i.e. the currently
850 862 in-process file), 'pos' is the current numeric position (i.e.
851 863 revision, bytes, etc.), unit is a corresponding unit label,
852 864 and total is the highest expected pos.
853 865
854 866 Multiple nested topics may be active at a time.
855 867
856 868 All topics should be marked closed by setting pos to None at
857 869 termination.
858 870 '''
859 871
860 872 if pos is None or not self.debugflag:
861 873 return
862 874
863 875 if unit:
864 876 unit = ' ' + unit
865 877 if item:
866 878 item = ' ' + item
867 879
868 880 if total:
869 881 pct = 100.0 * pos / total
870 882 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
871 883 % (topic, item, pos, total, unit, pct))
872 884 else:
873 885 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
874 886
875 887 def log(self, service, *msg, **opts):
876 888 '''hook for logging facility extensions
877 889
878 890 service should be a readily-identifiable subsystem, which will
879 891 allow filtering.
880 892 message should be a newline-terminated string to log.
881 893 '''
882 894 pass
883 895
884 896 def label(self, msg, label):
885 897 '''style msg based on supplied label
886 898
887 899 Like ui.write(), this just returns msg unchanged, but extensions
888 900 and GUI tools can override it to allow styling output without
889 901 writing it.
890 902
891 903 ui.write(s, 'label') is equivalent to
892 904 ui.write(ui.label(s, 'label')).
893 905 '''
894 906 return msg
General Comments 0
You need to be logged in to leave comments. Login now