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