##// END OF EJS Templates
paths: make getpath() accept multiple defaults...
Yuya Nishihara -
r27561:723413ee default
parent child Browse files
Show More
@@ -1,1271 +1,1274 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 __future__ import absolute_import
9 9
10 10 import errno
11 11 import getpass
12 12 import inspect
13 13 import os
14 14 import re
15 15 import socket
16 16 import sys
17 17 import tempfile
18 18 import traceback
19 19
20 20 from .i18n import _
21 21 from .node import hex
22 22
23 23 from . import (
24 24 config,
25 25 error,
26 26 formatter,
27 27 progress,
28 28 scmutil,
29 29 util,
30 30 )
31 31
32 32 samplehgrcs = {
33 33 'user':
34 34 """# example user config (see "hg help config" for more info)
35 35 [ui]
36 36 # name and email, e.g.
37 37 # username = Jane Doe <jdoe@example.com>
38 38 username =
39 39
40 40 [extensions]
41 41 # uncomment these lines to enable some popular extensions
42 42 # (see "hg help extensions" for more info)
43 43 #
44 44 # pager =
45 45 # progress =
46 46 # color =""",
47 47
48 48 'cloned':
49 49 """# example repository config (see "hg help config" for more info)
50 50 [paths]
51 51 default = %s
52 52
53 53 # path aliases to other clones of this repo in URLs or filesystem paths
54 54 # (see "hg help config.paths" for more info)
55 55 #
56 56 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
57 57 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
58 58 # my-clone = /home/jdoe/jdoes-clone
59 59
60 60 [ui]
61 61 # name and email (local to this repository, optional), e.g.
62 62 # username = Jane Doe <jdoe@example.com>
63 63 """,
64 64
65 65 'local':
66 66 """# example repository config (see "hg help config" for more info)
67 67 [paths]
68 68 # path aliases to other clones of this repo in URLs or filesystem paths
69 69 # (see "hg help config.paths" for more info)
70 70 #
71 71 # default = http://example.com/hg/example-repo
72 72 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
73 73 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
74 74 # my-clone = /home/jdoe/jdoes-clone
75 75
76 76 [ui]
77 77 # name and email (local to this repository, optional), e.g.
78 78 # username = Jane Doe <jdoe@example.com>
79 79 """,
80 80
81 81 'global':
82 82 """# example system-wide hg config (see "hg help config" for more info)
83 83
84 84 [extensions]
85 85 # uncomment these lines to enable some popular extensions
86 86 # (see "hg help extensions" for more info)
87 87 #
88 88 # blackbox =
89 89 # progress =
90 90 # color =
91 91 # pager =""",
92 92 }
93 93
94 94 class ui(object):
95 95 def __init__(self, src=None):
96 96 # _buffers: used for temporary capture of output
97 97 self._buffers = []
98 98 # 3-tuple describing how each buffer in the stack behaves.
99 99 # Values are (capture stderr, capture subprocesses, apply labels).
100 100 self._bufferstates = []
101 101 # When a buffer is active, defines whether we are expanding labels.
102 102 # This exists to prevent an extra list lookup.
103 103 self._bufferapplylabels = None
104 104 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
105 105 self._reportuntrusted = True
106 106 self._ocfg = config.config() # overlay
107 107 self._tcfg = config.config() # trusted
108 108 self._ucfg = config.config() # untrusted
109 109 self._trustusers = set()
110 110 self._trustgroups = set()
111 111 self.callhooks = True
112 112
113 113 if src:
114 114 self.fout = src.fout
115 115 self.ferr = src.ferr
116 116 self.fin = src.fin
117 117
118 118 self._tcfg = src._tcfg.copy()
119 119 self._ucfg = src._ucfg.copy()
120 120 self._ocfg = src._ocfg.copy()
121 121 self._trustusers = src._trustusers.copy()
122 122 self._trustgroups = src._trustgroups.copy()
123 123 self.environ = src.environ
124 124 self.callhooks = src.callhooks
125 125 self.fixconfig()
126 126 else:
127 127 self.fout = sys.stdout
128 128 self.ferr = sys.stderr
129 129 self.fin = sys.stdin
130 130
131 131 # shared read-only environment
132 132 self.environ = os.environ
133 133 # we always trust global config files
134 134 for f in scmutil.rcpath():
135 135 self.readconfig(f, trust=True)
136 136
137 137 def copy(self):
138 138 return self.__class__(self)
139 139
140 140 def formatter(self, topic, opts):
141 141 return formatter.formatter(self, topic, opts)
142 142
143 143 def _trusted(self, fp, f):
144 144 st = util.fstat(fp)
145 145 if util.isowner(st):
146 146 return True
147 147
148 148 tusers, tgroups = self._trustusers, self._trustgroups
149 149 if '*' in tusers or '*' in tgroups:
150 150 return True
151 151
152 152 user = util.username(st.st_uid)
153 153 group = util.groupname(st.st_gid)
154 154 if user in tusers or group in tgroups or user == util.username():
155 155 return True
156 156
157 157 if self._reportuntrusted:
158 158 self.warn(_('not trusting file %s from untrusted '
159 159 'user %s, group %s\n') % (f, user, group))
160 160 return False
161 161
162 162 def readconfig(self, filename, root=None, trust=False,
163 163 sections=None, remap=None):
164 164 try:
165 165 fp = open(filename)
166 166 except IOError:
167 167 if not sections: # ignore unless we were looking for something
168 168 return
169 169 raise
170 170
171 171 cfg = config.config()
172 172 trusted = sections or trust or self._trusted(fp, filename)
173 173
174 174 try:
175 175 cfg.read(filename, fp, sections=sections, remap=remap)
176 176 fp.close()
177 177 except error.ConfigError as inst:
178 178 if trusted:
179 179 raise
180 180 self.warn(_("ignored: %s\n") % str(inst))
181 181
182 182 if self.plain():
183 183 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
184 184 'logtemplate', 'statuscopies', 'style',
185 185 'traceback', 'verbose'):
186 186 if k in cfg['ui']:
187 187 del cfg['ui'][k]
188 188 for k, v in cfg.items('defaults'):
189 189 del cfg['defaults'][k]
190 190 # Don't remove aliases from the configuration if in the exceptionlist
191 191 if self.plain('alias'):
192 192 for k, v in cfg.items('alias'):
193 193 del cfg['alias'][k]
194 194 if self.plain('revsetalias'):
195 195 for k, v in cfg.items('revsetalias'):
196 196 del cfg['revsetalias'][k]
197 197
198 198 if trusted:
199 199 self._tcfg.update(cfg)
200 200 self._tcfg.update(self._ocfg)
201 201 self._ucfg.update(cfg)
202 202 self._ucfg.update(self._ocfg)
203 203
204 204 if root is None:
205 205 root = os.path.expanduser('~')
206 206 self.fixconfig(root=root)
207 207
208 208 def fixconfig(self, root=None, section=None):
209 209 if section in (None, 'paths'):
210 210 # expand vars and ~
211 211 # translate paths relative to root (or home) into absolute paths
212 212 root = root or os.getcwd()
213 213 for c in self._tcfg, self._ucfg, self._ocfg:
214 214 for n, p in c.items('paths'):
215 215 if not p:
216 216 continue
217 217 if '%%' in p:
218 218 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
219 219 % (n, p, self.configsource('paths', n)))
220 220 p = p.replace('%%', '%')
221 221 p = util.expandpath(p)
222 222 if not util.hasscheme(p) and not os.path.isabs(p):
223 223 p = os.path.normpath(os.path.join(root, p))
224 224 c.set("paths", n, p)
225 225
226 226 if section in (None, 'ui'):
227 227 # update ui options
228 228 self.debugflag = self.configbool('ui', 'debug')
229 229 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
230 230 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
231 231 if self.verbose and self.quiet:
232 232 self.quiet = self.verbose = False
233 233 self._reportuntrusted = self.debugflag or self.configbool("ui",
234 234 "report_untrusted", True)
235 235 self.tracebackflag = self.configbool('ui', 'traceback', False)
236 236
237 237 if section in (None, 'trusted'):
238 238 # update trust information
239 239 self._trustusers.update(self.configlist('trusted', 'users'))
240 240 self._trustgroups.update(self.configlist('trusted', 'groups'))
241 241
242 242 def backupconfig(self, section, item):
243 243 return (self._ocfg.backup(section, item),
244 244 self._tcfg.backup(section, item),
245 245 self._ucfg.backup(section, item),)
246 246 def restoreconfig(self, data):
247 247 self._ocfg.restore(data[0])
248 248 self._tcfg.restore(data[1])
249 249 self._ucfg.restore(data[2])
250 250
251 251 def setconfig(self, section, name, value, source=''):
252 252 for cfg in (self._ocfg, self._tcfg, self._ucfg):
253 253 cfg.set(section, name, value, source)
254 254 self.fixconfig(section=section)
255 255
256 256 def _data(self, untrusted):
257 257 return untrusted and self._ucfg or self._tcfg
258 258
259 259 def configsource(self, section, name, untrusted=False):
260 260 return self._data(untrusted).source(section, name) or 'none'
261 261
262 262 def config(self, section, name, default=None, untrusted=False):
263 263 if isinstance(name, list):
264 264 alternates = name
265 265 else:
266 266 alternates = [name]
267 267
268 268 for n in alternates:
269 269 value = self._data(untrusted).get(section, n, None)
270 270 if value is not None:
271 271 name = n
272 272 break
273 273 else:
274 274 value = default
275 275
276 276 if self.debugflag and not untrusted and self._reportuntrusted:
277 277 for n in alternates:
278 278 uvalue = self._ucfg.get(section, n)
279 279 if uvalue is not None and uvalue != value:
280 280 self.debug("ignoring untrusted configuration option "
281 281 "%s.%s = %s\n" % (section, n, uvalue))
282 282 return value
283 283
284 284 def configsuboptions(self, section, name, default=None, untrusted=False):
285 285 """Get a config option and all sub-options.
286 286
287 287 Some config options have sub-options that are declared with the
288 288 format "key:opt = value". This method is used to return the main
289 289 option and all its declared sub-options.
290 290
291 291 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
292 292 is a dict of defined sub-options where keys and values are strings.
293 293 """
294 294 data = self._data(untrusted)
295 295 main = data.get(section, name, default)
296 296 if self.debugflag and not untrusted and self._reportuntrusted:
297 297 uvalue = self._ucfg.get(section, name)
298 298 if uvalue is not None and uvalue != main:
299 299 self.debug('ignoring untrusted configuration option '
300 300 '%s.%s = %s\n' % (section, name, uvalue))
301 301
302 302 sub = {}
303 303 prefix = '%s:' % name
304 304 for k, v in data.items(section):
305 305 if k.startswith(prefix):
306 306 sub[k[len(prefix):]] = v
307 307
308 308 if self.debugflag and not untrusted and self._reportuntrusted:
309 309 for k, v in sub.items():
310 310 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
311 311 if uvalue is not None and uvalue != v:
312 312 self.debug('ignoring untrusted configuration option '
313 313 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
314 314
315 315 return main, sub
316 316
317 317 def configpath(self, section, name, default=None, untrusted=False):
318 318 'get a path config item, expanded relative to repo root or config file'
319 319 v = self.config(section, name, default, untrusted)
320 320 if v is None:
321 321 return None
322 322 if not os.path.isabs(v) or "://" not in v:
323 323 src = self.configsource(section, name, untrusted)
324 324 if ':' in src:
325 325 base = os.path.dirname(src.rsplit(':')[0])
326 326 v = os.path.join(base, os.path.expanduser(v))
327 327 return v
328 328
329 329 def configbool(self, section, name, default=False, untrusted=False):
330 330 """parse a configuration element as a boolean
331 331
332 332 >>> u = ui(); s = 'foo'
333 333 >>> u.setconfig(s, 'true', 'yes')
334 334 >>> u.configbool(s, 'true')
335 335 True
336 336 >>> u.setconfig(s, 'false', 'no')
337 337 >>> u.configbool(s, 'false')
338 338 False
339 339 >>> u.configbool(s, 'unknown')
340 340 False
341 341 >>> u.configbool(s, 'unknown', True)
342 342 True
343 343 >>> u.setconfig(s, 'invalid', 'somevalue')
344 344 >>> u.configbool(s, 'invalid')
345 345 Traceback (most recent call last):
346 346 ...
347 347 ConfigError: foo.invalid is not a boolean ('somevalue')
348 348 """
349 349
350 350 v = self.config(section, name, None, untrusted)
351 351 if v is None:
352 352 return default
353 353 if isinstance(v, bool):
354 354 return v
355 355 b = util.parsebool(v)
356 356 if b is None:
357 357 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
358 358 % (section, name, v))
359 359 return b
360 360
361 361 def configint(self, section, name, default=None, untrusted=False):
362 362 """parse a configuration element as an integer
363 363
364 364 >>> u = ui(); s = 'foo'
365 365 >>> u.setconfig(s, 'int1', '42')
366 366 >>> u.configint(s, 'int1')
367 367 42
368 368 >>> u.setconfig(s, 'int2', '-42')
369 369 >>> u.configint(s, 'int2')
370 370 -42
371 371 >>> u.configint(s, 'unknown', 7)
372 372 7
373 373 >>> u.setconfig(s, 'invalid', 'somevalue')
374 374 >>> u.configint(s, 'invalid')
375 375 Traceback (most recent call last):
376 376 ...
377 377 ConfigError: foo.invalid is not an integer ('somevalue')
378 378 """
379 379
380 380 v = self.config(section, name, None, untrusted)
381 381 if v is None:
382 382 return default
383 383 try:
384 384 return int(v)
385 385 except ValueError:
386 386 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
387 387 % (section, name, v))
388 388
389 389 def configbytes(self, section, name, default=0, untrusted=False):
390 390 """parse a configuration element as a quantity in bytes
391 391
392 392 Units can be specified as b (bytes), k or kb (kilobytes), m or
393 393 mb (megabytes), g or gb (gigabytes).
394 394
395 395 >>> u = ui(); s = 'foo'
396 396 >>> u.setconfig(s, 'val1', '42')
397 397 >>> u.configbytes(s, 'val1')
398 398 42
399 399 >>> u.setconfig(s, 'val2', '42.5 kb')
400 400 >>> u.configbytes(s, 'val2')
401 401 43520
402 402 >>> u.configbytes(s, 'unknown', '7 MB')
403 403 7340032
404 404 >>> u.setconfig(s, 'invalid', 'somevalue')
405 405 >>> u.configbytes(s, 'invalid')
406 406 Traceback (most recent call last):
407 407 ...
408 408 ConfigError: foo.invalid is not a byte quantity ('somevalue')
409 409 """
410 410
411 411 value = self.config(section, name)
412 412 if value is None:
413 413 if not isinstance(default, str):
414 414 return default
415 415 value = default
416 416 try:
417 417 return util.sizetoint(value)
418 418 except error.ParseError:
419 419 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
420 420 % (section, name, value))
421 421
422 422 def configlist(self, section, name, default=None, untrusted=False):
423 423 """parse a configuration element as a list of comma/space separated
424 424 strings
425 425
426 426 >>> u = ui(); s = 'foo'
427 427 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
428 428 >>> u.configlist(s, 'list1')
429 429 ['this', 'is', 'a small', 'test']
430 430 """
431 431
432 432 def _parse_plain(parts, s, offset):
433 433 whitespace = False
434 434 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
435 435 whitespace = True
436 436 offset += 1
437 437 if offset >= len(s):
438 438 return None, parts, offset
439 439 if whitespace:
440 440 parts.append('')
441 441 if s[offset] == '"' and not parts[-1]:
442 442 return _parse_quote, parts, offset + 1
443 443 elif s[offset] == '"' and parts[-1][-1] == '\\':
444 444 parts[-1] = parts[-1][:-1] + s[offset]
445 445 return _parse_plain, parts, offset + 1
446 446 parts[-1] += s[offset]
447 447 return _parse_plain, parts, offset + 1
448 448
449 449 def _parse_quote(parts, s, offset):
450 450 if offset < len(s) and s[offset] == '"': # ""
451 451 parts.append('')
452 452 offset += 1
453 453 while offset < len(s) and (s[offset].isspace() or
454 454 s[offset] == ','):
455 455 offset += 1
456 456 return _parse_plain, parts, offset
457 457
458 458 while offset < len(s) and s[offset] != '"':
459 459 if (s[offset] == '\\' and offset + 1 < len(s)
460 460 and s[offset + 1] == '"'):
461 461 offset += 1
462 462 parts[-1] += '"'
463 463 else:
464 464 parts[-1] += s[offset]
465 465 offset += 1
466 466
467 467 if offset >= len(s):
468 468 real_parts = _configlist(parts[-1])
469 469 if not real_parts:
470 470 parts[-1] = '"'
471 471 else:
472 472 real_parts[0] = '"' + real_parts[0]
473 473 parts = parts[:-1]
474 474 parts.extend(real_parts)
475 475 return None, parts, offset
476 476
477 477 offset += 1
478 478 while offset < len(s) and s[offset] in [' ', ',']:
479 479 offset += 1
480 480
481 481 if offset < len(s):
482 482 if offset + 1 == len(s) and s[offset] == '"':
483 483 parts[-1] += '"'
484 484 offset += 1
485 485 else:
486 486 parts.append('')
487 487 else:
488 488 return None, parts, offset
489 489
490 490 return _parse_plain, parts, offset
491 491
492 492 def _configlist(s):
493 493 s = s.rstrip(' ,')
494 494 if not s:
495 495 return []
496 496 parser, parts, offset = _parse_plain, [''], 0
497 497 while parser:
498 498 parser, parts, offset = parser(parts, s, offset)
499 499 return parts
500 500
501 501 result = self.config(section, name, untrusted=untrusted)
502 502 if result is None:
503 503 result = default or []
504 504 if isinstance(result, basestring):
505 505 result = _configlist(result.lstrip(' ,\n'))
506 506 if result is None:
507 507 result = default or []
508 508 return result
509 509
510 510 def has_section(self, section, untrusted=False):
511 511 '''tell whether section exists in config.'''
512 512 return section in self._data(untrusted)
513 513
514 514 def configitems(self, section, untrusted=False, ignoresub=False):
515 515 items = self._data(untrusted).items(section)
516 516 if ignoresub:
517 517 newitems = {}
518 518 for k, v in items:
519 519 if ':' not in k:
520 520 newitems[k] = v
521 521 items = newitems.items()
522 522 if self.debugflag and not untrusted and self._reportuntrusted:
523 523 for k, v in self._ucfg.items(section):
524 524 if self._tcfg.get(section, k) != v:
525 525 self.debug("ignoring untrusted configuration option "
526 526 "%s.%s = %s\n" % (section, k, v))
527 527 return items
528 528
529 529 def walkconfig(self, untrusted=False):
530 530 cfg = self._data(untrusted)
531 531 for section in cfg.sections():
532 532 for name, value in self.configitems(section, untrusted):
533 533 yield section, name, value
534 534
535 535 def plain(self, feature=None):
536 536 '''is plain mode active?
537 537
538 538 Plain mode means that all configuration variables which affect
539 539 the behavior and output of Mercurial should be
540 540 ignored. Additionally, the output should be stable,
541 541 reproducible and suitable for use in scripts or applications.
542 542
543 543 The only way to trigger plain mode is by setting either the
544 544 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
545 545
546 546 The return value can either be
547 547 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
548 548 - True otherwise
549 549 '''
550 550 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
551 551 return False
552 552 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
553 553 if feature and exceptions:
554 554 return feature not in exceptions
555 555 return True
556 556
557 557 def username(self):
558 558 """Return default username to be used in commits.
559 559
560 560 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
561 561 and stop searching if one of these is set.
562 562 If not found and ui.askusername is True, ask the user, else use
563 563 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
564 564 """
565 565 user = os.environ.get("HGUSER")
566 566 if user is None:
567 567 user = self.config("ui", ["username", "user"])
568 568 if user is not None:
569 569 user = os.path.expandvars(user)
570 570 if user is None:
571 571 user = os.environ.get("EMAIL")
572 572 if user is None and self.configbool("ui", "askusername"):
573 573 user = self.prompt(_("enter a commit username:"), default=None)
574 574 if user is None and not self.interactive():
575 575 try:
576 576 user = '%s@%s' % (util.getuser(), socket.getfqdn())
577 577 self.warn(_("no username found, using '%s' instead\n") % user)
578 578 except KeyError:
579 579 pass
580 580 if not user:
581 581 raise error.Abort(_('no username supplied'),
582 582 hint=_('use "hg config --edit" '
583 583 'to set your username'))
584 584 if "\n" in user:
585 585 raise error.Abort(_("username %s contains a newline\n")
586 586 % repr(user))
587 587 return user
588 588
589 589 def shortuser(self, user):
590 590 """Return a short representation of a user name or email address."""
591 591 if not self.verbose:
592 592 user = util.shortuser(user)
593 593 return user
594 594
595 595 def expandpath(self, loc, default=None):
596 596 """Return repository location relative to cwd or from [paths]"""
597 597 try:
598 598 p = self.paths.getpath(loc)
599 599 if p:
600 600 return p.rawloc
601 601 except error.RepoError:
602 602 pass
603 603
604 604 if default:
605 605 try:
606 606 p = self.paths.getpath(default)
607 607 if p:
608 608 return p.rawloc
609 609 except error.RepoError:
610 610 pass
611 611
612 612 return loc
613 613
614 614 @util.propertycache
615 615 def paths(self):
616 616 return paths(self)
617 617
618 618 def pushbuffer(self, error=False, subproc=False, labeled=False):
619 619 """install a buffer to capture standard output of the ui object
620 620
621 621 If error is True, the error output will be captured too.
622 622
623 623 If subproc is True, output from subprocesses (typically hooks) will be
624 624 captured too.
625 625
626 626 If labeled is True, any labels associated with buffered
627 627 output will be handled. By default, this has no effect
628 628 on the output returned, but extensions and GUI tools may
629 629 handle this argument and returned styled output. If output
630 630 is being buffered so it can be captured and parsed or
631 631 processed, labeled should not be set to True.
632 632 """
633 633 self._buffers.append([])
634 634 self._bufferstates.append((error, subproc, labeled))
635 635 self._bufferapplylabels = labeled
636 636
637 637 def popbuffer(self):
638 638 '''pop the last buffer and return the buffered output'''
639 639 self._bufferstates.pop()
640 640 if self._bufferstates:
641 641 self._bufferapplylabels = self._bufferstates[-1][2]
642 642 else:
643 643 self._bufferapplylabels = None
644 644
645 645 return "".join(self._buffers.pop())
646 646
647 647 def write(self, *args, **opts):
648 648 '''write args to output
649 649
650 650 By default, this method simply writes to the buffer or stdout,
651 651 but extensions or GUI tools may override this method,
652 652 write_err(), popbuffer(), and label() to style output from
653 653 various parts of hg.
654 654
655 655 An optional keyword argument, "label", can be passed in.
656 656 This should be a string containing label names separated by
657 657 space. Label names take the form of "topic.type". For example,
658 658 ui.debug() issues a label of "ui.debug".
659 659
660 660 When labeling output for a specific command, a label of
661 661 "cmdname.type" is recommended. For example, status issues
662 662 a label of "status.modified" for modified files.
663 663 '''
664 664 if self._buffers:
665 665 self._buffers[-1].extend(a for a in args)
666 666 else:
667 667 self._progclear()
668 668 for a in args:
669 669 self.fout.write(a)
670 670
671 671 def write_err(self, *args, **opts):
672 672 self._progclear()
673 673 try:
674 674 if self._bufferstates and self._bufferstates[-1][0]:
675 675 return self.write(*args, **opts)
676 676 if not getattr(self.fout, 'closed', False):
677 677 self.fout.flush()
678 678 for a in args:
679 679 self.ferr.write(a)
680 680 # stderr may be buffered under win32 when redirected to files,
681 681 # including stdout.
682 682 if not getattr(self.ferr, 'closed', False):
683 683 self.ferr.flush()
684 684 except IOError as inst:
685 685 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
686 686 raise
687 687
688 688 def flush(self):
689 689 try: self.fout.flush()
690 690 except (IOError, ValueError): pass
691 691 try: self.ferr.flush()
692 692 except (IOError, ValueError): pass
693 693
694 694 def _isatty(self, fh):
695 695 if self.configbool('ui', 'nontty', False):
696 696 return False
697 697 return util.isatty(fh)
698 698
699 699 def interactive(self):
700 700 '''is interactive input allowed?
701 701
702 702 An interactive session is a session where input can be reasonably read
703 703 from `sys.stdin'. If this function returns false, any attempt to read
704 704 from stdin should fail with an error, unless a sensible default has been
705 705 specified.
706 706
707 707 Interactiveness is triggered by the value of the `ui.interactive'
708 708 configuration variable or - if it is unset - when `sys.stdin' points
709 709 to a terminal device.
710 710
711 711 This function refers to input only; for output, see `ui.formatted()'.
712 712 '''
713 713 i = self.configbool("ui", "interactive", None)
714 714 if i is None:
715 715 # some environments replace stdin without implementing isatty
716 716 # usually those are non-interactive
717 717 return self._isatty(self.fin)
718 718
719 719 return i
720 720
721 721 def termwidth(self):
722 722 '''how wide is the terminal in columns?
723 723 '''
724 724 if 'COLUMNS' in os.environ:
725 725 try:
726 726 return int(os.environ['COLUMNS'])
727 727 except ValueError:
728 728 pass
729 729 return util.termwidth()
730 730
731 731 def formatted(self):
732 732 '''should formatted output be used?
733 733
734 734 It is often desirable to format the output to suite the output medium.
735 735 Examples of this are truncating long lines or colorizing messages.
736 736 However, this is not often not desirable when piping output into other
737 737 utilities, e.g. `grep'.
738 738
739 739 Formatted output is triggered by the value of the `ui.formatted'
740 740 configuration variable or - if it is unset - when `sys.stdout' points
741 741 to a terminal device. Please note that `ui.formatted' should be
742 742 considered an implementation detail; it is not intended for use outside
743 743 Mercurial or its extensions.
744 744
745 745 This function refers to output only; for input, see `ui.interactive()'.
746 746 This function always returns false when in plain mode, see `ui.plain()'.
747 747 '''
748 748 if self.plain():
749 749 return False
750 750
751 751 i = self.configbool("ui", "formatted", None)
752 752 if i is None:
753 753 # some environments replace stdout without implementing isatty
754 754 # usually those are non-interactive
755 755 return self._isatty(self.fout)
756 756
757 757 return i
758 758
759 759 def _readline(self, prompt=''):
760 760 if self._isatty(self.fin):
761 761 try:
762 762 # magically add command line editing support, where
763 763 # available
764 764 import readline
765 765 # force demandimport to really load the module
766 766 readline.read_history_file
767 767 # windows sometimes raises something other than ImportError
768 768 except Exception:
769 769 pass
770 770
771 771 # call write() so output goes through subclassed implementation
772 772 # e.g. color extension on Windows
773 773 self.write(prompt)
774 774
775 775 # instead of trying to emulate raw_input, swap (self.fin,
776 776 # self.fout) with (sys.stdin, sys.stdout)
777 777 oldin = sys.stdin
778 778 oldout = sys.stdout
779 779 sys.stdin = self.fin
780 780 sys.stdout = self.fout
781 781 # prompt ' ' must exist; otherwise readline may delete entire line
782 782 # - http://bugs.python.org/issue12833
783 783 line = raw_input(' ')
784 784 sys.stdin = oldin
785 785 sys.stdout = oldout
786 786
787 787 # When stdin is in binary mode on Windows, it can cause
788 788 # raw_input() to emit an extra trailing carriage return
789 789 if os.linesep == '\r\n' and line and line[-1] == '\r':
790 790 line = line[:-1]
791 791 return line
792 792
793 793 def prompt(self, msg, default="y"):
794 794 """Prompt user with msg, read response.
795 795 If ui is not interactive, the default is returned.
796 796 """
797 797 if not self.interactive():
798 798 self.write(msg, ' ', default, "\n")
799 799 return default
800 800 try:
801 801 r = self._readline(self.label(msg, 'ui.prompt'))
802 802 if not r:
803 803 r = default
804 804 if self.configbool('ui', 'promptecho'):
805 805 self.write(r, "\n")
806 806 return r
807 807 except EOFError:
808 808 raise error.ResponseExpected()
809 809
810 810 @staticmethod
811 811 def extractchoices(prompt):
812 812 """Extract prompt message and list of choices from specified prompt.
813 813
814 814 This returns tuple "(message, choices)", and "choices" is the
815 815 list of tuple "(response character, text without &)".
816 816
817 817 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
818 818 ('awake? ', [('y', 'Yes'), ('n', 'No')])
819 819 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
820 820 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
821 821 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
822 822 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
823 823 """
824 824
825 825 # Sadly, the prompt string may have been built with a filename
826 826 # containing "$$" so let's try to find the first valid-looking
827 827 # prompt to start parsing. Sadly, we also can't rely on
828 828 # choices containing spaces, ASCII, or basically anything
829 829 # except an ampersand followed by a character.
830 830 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
831 831 msg = m.group(1)
832 832 choices = [p.strip(' ') for p in m.group(2).split('$$')]
833 833 return (msg,
834 834 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
835 835 for s in choices])
836 836
837 837 def promptchoice(self, prompt, default=0):
838 838 """Prompt user with a message, read response, and ensure it matches
839 839 one of the provided choices. The prompt is formatted as follows:
840 840
841 841 "would you like fries with that (Yn)? $$ &Yes $$ &No"
842 842
843 843 The index of the choice is returned. Responses are case
844 844 insensitive. If ui is not interactive, the default is
845 845 returned.
846 846 """
847 847
848 848 msg, choices = self.extractchoices(prompt)
849 849 resps = [r for r, t in choices]
850 850 while True:
851 851 r = self.prompt(msg, resps[default])
852 852 if r.lower() in resps:
853 853 return resps.index(r.lower())
854 854 self.write(_("unrecognized response\n"))
855 855
856 856 def getpass(self, prompt=None, default=None):
857 857 if not self.interactive():
858 858 return default
859 859 try:
860 860 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
861 861 # disable getpass() only if explicitly specified. it's still valid
862 862 # to interact with tty even if fin is not a tty.
863 863 if self.configbool('ui', 'nontty'):
864 864 return self.fin.readline().rstrip('\n')
865 865 else:
866 866 return getpass.getpass('')
867 867 except EOFError:
868 868 raise error.ResponseExpected()
869 869 def status(self, *msg, **opts):
870 870 '''write status message to output (if ui.quiet is False)
871 871
872 872 This adds an output label of "ui.status".
873 873 '''
874 874 if not self.quiet:
875 875 opts['label'] = opts.get('label', '') + ' ui.status'
876 876 self.write(*msg, **opts)
877 877 def warn(self, *msg, **opts):
878 878 '''write warning message to output (stderr)
879 879
880 880 This adds an output label of "ui.warning".
881 881 '''
882 882 opts['label'] = opts.get('label', '') + ' ui.warning'
883 883 self.write_err(*msg, **opts)
884 884 def note(self, *msg, **opts):
885 885 '''write note to output (if ui.verbose is True)
886 886
887 887 This adds an output label of "ui.note".
888 888 '''
889 889 if self.verbose:
890 890 opts['label'] = opts.get('label', '') + ' ui.note'
891 891 self.write(*msg, **opts)
892 892 def debug(self, *msg, **opts):
893 893 '''write debug message to output (if ui.debugflag is True)
894 894
895 895 This adds an output label of "ui.debug".
896 896 '''
897 897 if self.debugflag:
898 898 opts['label'] = opts.get('label', '') + ' ui.debug'
899 899 self.write(*msg, **opts)
900 900
901 901 def edit(self, text, user, extra=None, editform=None, pending=None):
902 902 extra_defaults = { 'prefix': 'editor' }
903 903 if extra is not None:
904 904 extra_defaults.update(extra)
905 905 extra = extra_defaults
906 906 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
907 907 suffix=".txt", text=True)
908 908 try:
909 909 f = os.fdopen(fd, "w")
910 910 f.write(text)
911 911 f.close()
912 912
913 913 environ = {'HGUSER': user}
914 914 if 'transplant_source' in extra:
915 915 environ.update({'HGREVISION': hex(extra['transplant_source'])})
916 916 for label in ('intermediate-source', 'source', 'rebase_source'):
917 917 if label in extra:
918 918 environ.update({'HGREVISION': extra[label]})
919 919 break
920 920 if editform:
921 921 environ.update({'HGEDITFORM': editform})
922 922 if pending:
923 923 environ.update({'HG_PENDING': pending})
924 924
925 925 editor = self.geteditor()
926 926
927 927 self.system("%s \"%s\"" % (editor, name),
928 928 environ=environ,
929 929 onerr=error.Abort, errprefix=_("edit failed"))
930 930
931 931 f = open(name)
932 932 t = f.read()
933 933 f.close()
934 934 finally:
935 935 os.unlink(name)
936 936
937 937 return t
938 938
939 939 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
940 940 '''execute shell command with appropriate output stream. command
941 941 output will be redirected if fout is not stdout.
942 942 '''
943 943 out = self.fout
944 944 if any(s[1] for s in self._bufferstates):
945 945 out = self
946 946 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
947 947 errprefix=errprefix, out=out)
948 948
949 949 def traceback(self, exc=None, force=False):
950 950 '''print exception traceback if traceback printing enabled or forced.
951 951 only to call in exception handler. returns true if traceback
952 952 printed.'''
953 953 if self.tracebackflag or force:
954 954 if exc is None:
955 955 exc = sys.exc_info()
956 956 cause = getattr(exc[1], 'cause', None)
957 957
958 958 if cause is not None:
959 959 causetb = traceback.format_tb(cause[2])
960 960 exctb = traceback.format_tb(exc[2])
961 961 exconly = traceback.format_exception_only(cause[0], cause[1])
962 962
963 963 # exclude frame where 'exc' was chained and rethrown from exctb
964 964 self.write_err('Traceback (most recent call last):\n',
965 965 ''.join(exctb[:-1]),
966 966 ''.join(causetb),
967 967 ''.join(exconly))
968 968 else:
969 969 output = traceback.format_exception(exc[0], exc[1], exc[2])
970 970 self.write_err(''.join(output))
971 971 return self.tracebackflag or force
972 972
973 973 def geteditor(self):
974 974 '''return editor to use'''
975 975 if sys.platform == 'plan9':
976 976 # vi is the MIPS instruction simulator on Plan 9. We
977 977 # instead default to E to plumb commit messages to
978 978 # avoid confusion.
979 979 editor = 'E'
980 980 else:
981 981 editor = 'vi'
982 982 return (os.environ.get("HGEDITOR") or
983 983 self.config("ui", "editor") or
984 984 os.environ.get("VISUAL") or
985 985 os.environ.get("EDITOR", editor))
986 986
987 987 @util.propertycache
988 988 def _progbar(self):
989 989 """setup the progbar singleton to the ui object"""
990 990 if (self.quiet or self.debugflag
991 991 or self.configbool('progress', 'disable', False)
992 992 or not progress.shouldprint(self)):
993 993 return None
994 994 return getprogbar(self)
995 995
996 996 def _progclear(self):
997 997 """clear progress bar output if any. use it before any output"""
998 998 if '_progbar' not in vars(self): # nothing loaded yet
999 999 return
1000 1000 if self._progbar is not None and self._progbar.printed:
1001 1001 self._progbar.clear()
1002 1002
1003 1003 def progress(self, topic, pos, item="", unit="", total=None):
1004 1004 '''show a progress message
1005 1005
1006 1006 With stock hg, this is simply a debug message that is hidden
1007 1007 by default, but with extensions or GUI tools it may be
1008 1008 visible. 'topic' is the current operation, 'item' is a
1009 1009 non-numeric marker of the current position (i.e. the currently
1010 1010 in-process file), 'pos' is the current numeric position (i.e.
1011 1011 revision, bytes, etc.), unit is a corresponding unit label,
1012 1012 and total is the highest expected pos.
1013 1013
1014 1014 Multiple nested topics may be active at a time.
1015 1015
1016 1016 All topics should be marked closed by setting pos to None at
1017 1017 termination.
1018 1018 '''
1019 1019 if self._progbar is not None:
1020 1020 self._progbar.progress(topic, pos, item=item, unit=unit,
1021 1021 total=total)
1022 1022 if pos is None or not self.configbool('progress', 'debug'):
1023 1023 return
1024 1024
1025 1025 if unit:
1026 1026 unit = ' ' + unit
1027 1027 if item:
1028 1028 item = ' ' + item
1029 1029
1030 1030 if total:
1031 1031 pct = 100.0 * pos / total
1032 1032 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1033 1033 % (topic, item, pos, total, unit, pct))
1034 1034 else:
1035 1035 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1036 1036
1037 1037 def log(self, service, *msg, **opts):
1038 1038 '''hook for logging facility extensions
1039 1039
1040 1040 service should be a readily-identifiable subsystem, which will
1041 1041 allow filtering.
1042 1042
1043 1043 *msg should be a newline-terminated format string to log, and
1044 1044 then any values to %-format into that format string.
1045 1045
1046 1046 **opts currently has no defined meanings.
1047 1047 '''
1048 1048
1049 1049 def label(self, msg, label):
1050 1050 '''style msg based on supplied label
1051 1051
1052 1052 Like ui.write(), this just returns msg unchanged, but extensions
1053 1053 and GUI tools can override it to allow styling output without
1054 1054 writing it.
1055 1055
1056 1056 ui.write(s, 'label') is equivalent to
1057 1057 ui.write(ui.label(s, 'label')).
1058 1058 '''
1059 1059 return msg
1060 1060
1061 1061 def develwarn(self, msg, stacklevel=1):
1062 1062 """issue a developer warning message
1063 1063
1064 1064 Use 'stacklevel' to report the offender some layers further up in the
1065 1065 stack.
1066 1066 """
1067 1067 msg = 'devel-warn: ' + msg
1068 1068 stacklevel += 1 # get in develwarn
1069 1069 if self.tracebackflag:
1070 1070 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1071 1071 else:
1072 1072 curframe = inspect.currentframe()
1073 1073 calframe = inspect.getouterframes(curframe, 2)
1074 1074 self.write_err('%s at: %s:%s (%s)\n'
1075 1075 % ((msg,) + calframe[stacklevel][1:4]))
1076 1076
1077 1077 def deprecwarn(self, msg, version):
1078 1078 """issue a deprecation warning
1079 1079
1080 1080 - msg: message explaining what is deprecated and how to upgrade,
1081 1081 - version: last version where the API will be supported,
1082 1082 """
1083 1083 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1084 1084 " update your code.)") % version
1085 1085 self.develwarn(msg, stacklevel=2)
1086 1086
1087 1087 class paths(dict):
1088 1088 """Represents a collection of paths and their configs.
1089 1089
1090 1090 Data is initially derived from ui instances and the config files they have
1091 1091 loaded.
1092 1092 """
1093 1093 def __init__(self, ui):
1094 1094 dict.__init__(self)
1095 1095
1096 1096 for name, loc in ui.configitems('paths', ignoresub=True):
1097 1097 # No location is the same as not existing.
1098 1098 if not loc:
1099 1099 continue
1100 1100
1101 1101 # TODO ignore default-push once all consumers stop referencing it
1102 1102 # since it is handled specifically below.
1103 1103
1104 1104 loc, sub = ui.configsuboptions('paths', name)
1105 1105 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1106 1106
1107 1107 # Handle default-push, which is a one-off that defines the push URL for
1108 1108 # the "default" path.
1109 1109 defaultpush = ui.config('paths', 'default-push')
1110 1110 if defaultpush:
1111 1111 # "default-push" can be defined without "default" entry. This is a
1112 1112 # bit weird, but is allowed for backwards compatibility.
1113 1113 if 'default' not in self:
1114 1114 self['default'] = path(ui, 'default', rawloc=defaultpush)
1115 1115 self['default'].pushloc = defaultpush
1116 1116
1117 1117 def getpath(self, name, default=None):
1118 """Return a ``path`` from a string, falling back to a default.
1118 """Return a ``path`` from a string, falling back to default.
1119 1119
1120 1120 ``name`` can be a named path or locations. Locations are filesystem
1121 1121 paths or URIs.
1122 1122
1123 1123 Returns None if ``name`` is not a registered path, a URI, or a local
1124 1124 path to a repo.
1125 1125 """
1126 1126 # Only fall back to default if no path was requested.
1127 1127 if name is None:
1128 if default:
1128 if not default:
1129 default = ()
1130 elif not isinstance(default, (tuple, list)):
1131 default = (default,)
1132 for k in default:
1129 1133 try:
1130 return self[default]
1134 return self[k]
1131 1135 except KeyError:
1132 return None
1133 else:
1134 return None
1136 continue
1137 return None
1135 1138
1136 1139 # Most likely empty string.
1137 1140 # This may need to raise in the future.
1138 1141 if not name:
1139 1142 return None
1140 1143
1141 1144 try:
1142 1145 return self[name]
1143 1146 except KeyError:
1144 1147 # Try to resolve as a local path or URI.
1145 1148 try:
1146 1149 # We don't pass sub-options in, so no need to pass ui instance.
1147 1150 return path(None, None, rawloc=name)
1148 1151 except ValueError:
1149 1152 raise error.RepoError(_('repository %s does not exist') %
1150 1153 name)
1151 1154
1152 1155 _pathsuboptions = {}
1153 1156
1154 1157 def pathsuboption(option, attr):
1155 1158 """Decorator used to declare a path sub-option.
1156 1159
1157 1160 Arguments are the sub-option name and the attribute it should set on
1158 1161 ``path`` instances.
1159 1162
1160 1163 The decorated function will receive as arguments a ``ui`` instance,
1161 1164 ``path`` instance, and the string value of this option from the config.
1162 1165 The function should return the value that will be set on the ``path``
1163 1166 instance.
1164 1167
1165 1168 This decorator can be used to perform additional verification of
1166 1169 sub-options and to change the type of sub-options.
1167 1170 """
1168 1171 def register(func):
1169 1172 _pathsuboptions[option] = (attr, func)
1170 1173 return func
1171 1174 return register
1172 1175
1173 1176 @pathsuboption('pushurl', 'pushloc')
1174 1177 def pushurlpathoption(ui, path, value):
1175 1178 u = util.url(value)
1176 1179 # Actually require a URL.
1177 1180 if not u.scheme:
1178 1181 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1179 1182 return None
1180 1183
1181 1184 # Don't support the #foo syntax in the push URL to declare branch to
1182 1185 # push.
1183 1186 if u.fragment:
1184 1187 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1185 1188 'ignoring)\n') % path.name)
1186 1189 u.fragment = None
1187 1190
1188 1191 return str(u)
1189 1192
1190 1193 class path(object):
1191 1194 """Represents an individual path and its configuration."""
1192 1195
1193 1196 def __init__(self, ui, name, rawloc=None, suboptions=None):
1194 1197 """Construct a path from its config options.
1195 1198
1196 1199 ``ui`` is the ``ui`` instance the path is coming from.
1197 1200 ``name`` is the symbolic name of the path.
1198 1201 ``rawloc`` is the raw location, as defined in the config.
1199 1202 ``pushloc`` is the raw locations pushes should be made to.
1200 1203
1201 1204 If ``name`` is not defined, we require that the location be a) a local
1202 1205 filesystem path with a .hg directory or b) a URL. If not,
1203 1206 ``ValueError`` is raised.
1204 1207 """
1205 1208 if not rawloc:
1206 1209 raise ValueError('rawloc must be defined')
1207 1210
1208 1211 # Locations may define branches via syntax <base>#<branch>.
1209 1212 u = util.url(rawloc)
1210 1213 branch = None
1211 1214 if u.fragment:
1212 1215 branch = u.fragment
1213 1216 u.fragment = None
1214 1217
1215 1218 self.url = u
1216 1219 self.branch = branch
1217 1220
1218 1221 self.name = name
1219 1222 self.rawloc = rawloc
1220 1223 self.loc = str(u)
1221 1224
1222 1225 # When given a raw location but not a symbolic name, validate the
1223 1226 # location is valid.
1224 1227 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1225 1228 raise ValueError('location is not a URL or path to a local '
1226 1229 'repo: %s' % rawloc)
1227 1230
1228 1231 suboptions = suboptions or {}
1229 1232
1230 1233 # Now process the sub-options. If a sub-option is registered, its
1231 1234 # attribute will always be present. The value will be None if there
1232 1235 # was no valid sub-option.
1233 1236 for suboption, (attr, func) in _pathsuboptions.iteritems():
1234 1237 if suboption not in suboptions:
1235 1238 setattr(self, attr, None)
1236 1239 continue
1237 1240
1238 1241 value = func(ui, self, suboptions[suboption])
1239 1242 setattr(self, attr, value)
1240 1243
1241 1244 def _isvalidlocalpath(self, path):
1242 1245 """Returns True if the given path is a potentially valid repository.
1243 1246 This is its own function so that extensions can change the definition of
1244 1247 'valid' in this case (like when pulling from a git repo into a hg
1245 1248 one)."""
1246 1249 return os.path.isdir(os.path.join(path, '.hg'))
1247 1250
1248 1251 @property
1249 1252 def suboptions(self):
1250 1253 """Return sub-options and their values for this path.
1251 1254
1252 1255 This is intended to be used for presentation purposes.
1253 1256 """
1254 1257 d = {}
1255 1258 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1256 1259 value = getattr(self, attr)
1257 1260 if value is not None:
1258 1261 d[subopt] = value
1259 1262 return d
1260 1263
1261 1264 # we instantiate one globally shared progress bar to avoid
1262 1265 # competing progress bars when multiple UI objects get created
1263 1266 _progresssingleton = None
1264 1267
1265 1268 def getprogbar(ui):
1266 1269 global _progresssingleton
1267 1270 if _progresssingleton is None:
1268 1271 # passing 'ui' object to the singleton is fishy,
1269 1272 # this is how the extension used to work but feel free to rework it.
1270 1273 _progresssingleton = progress.progbar(ui)
1271 1274 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now