##// END OF EJS Templates
pager: improve support for various flavors of `more` on Windows...
Matt Harbison -
r31624:c60091fa default
parent child Browse files
Show More
@@ -1,1627 +1,1633
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 atexit
11 11 import collections
12 12 import contextlib
13 13 import errno
14 14 import getpass
15 15 import inspect
16 16 import os
17 17 import re
18 18 import signal
19 19 import socket
20 20 import subprocess
21 21 import sys
22 22 import tempfile
23 23 import traceback
24 24
25 25 from .i18n import _
26 26 from .node import hex
27 27
28 28 from . import (
29 29 color,
30 30 config,
31 31 encoding,
32 32 error,
33 33 formatter,
34 34 progress,
35 35 pycompat,
36 36 scmutil,
37 37 util,
38 38 )
39 39
40 40 urlreq = util.urlreq
41 41
42 42 # for use with str.translate(None, _keepalnum), to keep just alphanumerics
43 43 _keepalnum = ''.join(c for c in map(pycompat.bytechr, range(256))
44 44 if not c.isalnum())
45 45
46 46 samplehgrcs = {
47 47 'user':
48 48 """# example user config (see 'hg help config' for more info)
49 49 [ui]
50 50 # name and email, e.g.
51 51 # username = Jane Doe <jdoe@example.com>
52 52 username =
53 53
54 54 # uncomment to colorize command output
55 55 # color = auto
56 56
57 57 [extensions]
58 58 # uncomment these lines to enable some popular extensions
59 59 # (see 'hg help extensions' for more info)
60 60 #
61 61 # pager =""",
62 62
63 63 'cloned':
64 64 """# example repository config (see 'hg help config' for more info)
65 65 [paths]
66 66 default = %s
67 67
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:pushurl = 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 'local':
81 81 """# example repository config (see 'hg help config' for more info)
82 82 [paths]
83 83 # path aliases to other clones of this repo in URLs or filesystem paths
84 84 # (see 'hg help config.paths' for more info)
85 85 #
86 86 # default = http://example.com/hg/example-repo
87 87 # default:pushurl = ssh://jdoe@example.net/hg/jdoes-fork
88 88 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
89 89 # my-clone = /home/jdoe/jdoes-clone
90 90
91 91 [ui]
92 92 # name and email (local to this repository, optional), e.g.
93 93 # username = Jane Doe <jdoe@example.com>
94 94 """,
95 95
96 96 'global':
97 97 """# example system-wide hg config (see 'hg help config' for more info)
98 98
99 99 [ui]
100 100 # uncomment to colorize command output
101 101 # color = auto
102 102
103 103 [extensions]
104 104 # uncomment these lines to enable some popular extensions
105 105 # (see 'hg help extensions' for more info)
106 106 #
107 107 # blackbox =
108 108 # pager =""",
109 109 }
110 110
111 111
112 112 class httppasswordmgrdbproxy(object):
113 113 """Delays loading urllib2 until it's needed."""
114 114 def __init__(self):
115 115 self._mgr = None
116 116
117 117 def _get_mgr(self):
118 118 if self._mgr is None:
119 119 self._mgr = urlreq.httppasswordmgrwithdefaultrealm()
120 120 return self._mgr
121 121
122 122 def add_password(self, *args, **kwargs):
123 123 return self._get_mgr().add_password(*args, **kwargs)
124 124
125 125 def find_user_password(self, *args, **kwargs):
126 126 return self._get_mgr().find_user_password(*args, **kwargs)
127 127
128 128 def _catchterm(*args):
129 129 raise error.SignalInterrupt
130 130
131 131 class ui(object):
132 132 def __init__(self, src=None):
133 133 """Create a fresh new ui object if no src given
134 134
135 135 Use uimod.ui.load() to create a ui which knows global and user configs.
136 136 In most cases, you should use ui.copy() to create a copy of an existing
137 137 ui object.
138 138 """
139 139 # _buffers: used for temporary capture of output
140 140 self._buffers = []
141 141 # 3-tuple describing how each buffer in the stack behaves.
142 142 # Values are (capture stderr, capture subprocesses, apply labels).
143 143 self._bufferstates = []
144 144 # When a buffer is active, defines whether we are expanding labels.
145 145 # This exists to prevent an extra list lookup.
146 146 self._bufferapplylabels = None
147 147 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
148 148 self._reportuntrusted = True
149 149 self._ocfg = config.config() # overlay
150 150 self._tcfg = config.config() # trusted
151 151 self._ucfg = config.config() # untrusted
152 152 self._trustusers = set()
153 153 self._trustgroups = set()
154 154 self.callhooks = True
155 155 # Insecure server connections requested.
156 156 self.insecureconnections = False
157 157 # Blocked time
158 158 self.logblockedtimes = False
159 159 # color mode: see mercurial/color.py for possible value
160 160 self._colormode = None
161 161 self._terminfoparams = {}
162 162 self._styles = {}
163 163
164 164 if src:
165 165 self.fout = src.fout
166 166 self.ferr = src.ferr
167 167 self.fin = src.fin
168 168 self.pageractive = src.pageractive
169 169 self._disablepager = src._disablepager
170 170
171 171 self._tcfg = src._tcfg.copy()
172 172 self._ucfg = src._ucfg.copy()
173 173 self._ocfg = src._ocfg.copy()
174 174 self._trustusers = src._trustusers.copy()
175 175 self._trustgroups = src._trustgroups.copy()
176 176 self.environ = src.environ
177 177 self.callhooks = src.callhooks
178 178 self.insecureconnections = src.insecureconnections
179 179 self._colormode = src._colormode
180 180 self._terminfoparams = src._terminfoparams.copy()
181 181 self._styles = src._styles.copy()
182 182
183 183 self.fixconfig()
184 184
185 185 self.httppasswordmgrdb = src.httppasswordmgrdb
186 186 self._blockedtimes = src._blockedtimes
187 187 else:
188 188 self.fout = util.stdout
189 189 self.ferr = util.stderr
190 190 self.fin = util.stdin
191 191 self.pageractive = False
192 192 self._disablepager = False
193 193
194 194 # shared read-only environment
195 195 self.environ = encoding.environ
196 196
197 197 self.httppasswordmgrdb = httppasswordmgrdbproxy()
198 198 self._blockedtimes = collections.defaultdict(int)
199 199
200 200 allowed = self.configlist('experimental', 'exportableenviron')
201 201 if '*' in allowed:
202 202 self._exportableenviron = self.environ
203 203 else:
204 204 self._exportableenviron = {}
205 205 for k in allowed:
206 206 if k in self.environ:
207 207 self._exportableenviron[k] = self.environ[k]
208 208
209 209 @classmethod
210 210 def load(cls):
211 211 """Create a ui and load global and user configs"""
212 212 u = cls()
213 213 # we always trust global config files
214 214 for f in scmutil.rcpath():
215 215 u.readconfig(f, trust=True)
216 216 return u
217 217
218 218 def copy(self):
219 219 return self.__class__(self)
220 220
221 221 def resetstate(self):
222 222 """Clear internal state that shouldn't persist across commands"""
223 223 if self._progbar:
224 224 self._progbar.resetstate() # reset last-print time of progress bar
225 225 self.httppasswordmgrdb = httppasswordmgrdbproxy()
226 226
227 227 @contextlib.contextmanager
228 228 def timeblockedsection(self, key):
229 229 # this is open-coded below - search for timeblockedsection to find them
230 230 starttime = util.timer()
231 231 try:
232 232 yield
233 233 finally:
234 234 self._blockedtimes[key + '_blocked'] += \
235 235 (util.timer() - starttime) * 1000
236 236
237 237 def formatter(self, topic, opts):
238 238 return formatter.formatter(self, topic, opts)
239 239
240 240 def _trusted(self, fp, f):
241 241 st = util.fstat(fp)
242 242 if util.isowner(st):
243 243 return True
244 244
245 245 tusers, tgroups = self._trustusers, self._trustgroups
246 246 if '*' in tusers or '*' in tgroups:
247 247 return True
248 248
249 249 user = util.username(st.st_uid)
250 250 group = util.groupname(st.st_gid)
251 251 if user in tusers or group in tgroups or user == util.username():
252 252 return True
253 253
254 254 if self._reportuntrusted:
255 255 self.warn(_('not trusting file %s from untrusted '
256 256 'user %s, group %s\n') % (f, user, group))
257 257 return False
258 258
259 259 def readconfig(self, filename, root=None, trust=False,
260 260 sections=None, remap=None):
261 261 try:
262 262 fp = open(filename, u'rb')
263 263 except IOError:
264 264 if not sections: # ignore unless we were looking for something
265 265 return
266 266 raise
267 267
268 268 cfg = config.config()
269 269 trusted = sections or trust or self._trusted(fp, filename)
270 270
271 271 try:
272 272 cfg.read(filename, fp, sections=sections, remap=remap)
273 273 fp.close()
274 274 except error.ConfigError as inst:
275 275 if trusted:
276 276 raise
277 277 self.warn(_("ignored: %s\n") % str(inst))
278 278
279 279 if self.plain():
280 280 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
281 281 'logtemplate', 'statuscopies', 'style',
282 282 'traceback', 'verbose'):
283 283 if k in cfg['ui']:
284 284 del cfg['ui'][k]
285 285 for k, v in cfg.items('defaults'):
286 286 del cfg['defaults'][k]
287 287 for k, v in cfg.items('commands'):
288 288 del cfg['commands'][k]
289 289 # Don't remove aliases from the configuration if in the exceptionlist
290 290 if self.plain('alias'):
291 291 for k, v in cfg.items('alias'):
292 292 del cfg['alias'][k]
293 293 if self.plain('revsetalias'):
294 294 for k, v in cfg.items('revsetalias'):
295 295 del cfg['revsetalias'][k]
296 296 if self.plain('templatealias'):
297 297 for k, v in cfg.items('templatealias'):
298 298 del cfg['templatealias'][k]
299 299
300 300 if trusted:
301 301 self._tcfg.update(cfg)
302 302 self._tcfg.update(self._ocfg)
303 303 self._ucfg.update(cfg)
304 304 self._ucfg.update(self._ocfg)
305 305
306 306 if root is None:
307 307 root = os.path.expanduser('~')
308 308 self.fixconfig(root=root)
309 309
310 310 def fixconfig(self, root=None, section=None):
311 311 if section in (None, 'paths'):
312 312 # expand vars and ~
313 313 # translate paths relative to root (or home) into absolute paths
314 314 root = root or pycompat.getcwd()
315 315 for c in self._tcfg, self._ucfg, self._ocfg:
316 316 for n, p in c.items('paths'):
317 317 # Ignore sub-options.
318 318 if ':' in n:
319 319 continue
320 320 if not p:
321 321 continue
322 322 if '%%' in p:
323 323 s = self.configsource('paths', n) or 'none'
324 324 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
325 325 % (n, p, s))
326 326 p = p.replace('%%', '%')
327 327 p = util.expandpath(p)
328 328 if not util.hasscheme(p) and not os.path.isabs(p):
329 329 p = os.path.normpath(os.path.join(root, p))
330 330 c.set("paths", n, p)
331 331
332 332 if section in (None, 'ui'):
333 333 # update ui options
334 334 self.debugflag = self.configbool('ui', 'debug')
335 335 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
336 336 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
337 337 if self.verbose and self.quiet:
338 338 self.quiet = self.verbose = False
339 339 self._reportuntrusted = self.debugflag or self.configbool("ui",
340 340 "report_untrusted", True)
341 341 self.tracebackflag = self.configbool('ui', 'traceback', False)
342 342 self.logblockedtimes = self.configbool('ui', 'logblockedtimes')
343 343
344 344 if section in (None, 'trusted'):
345 345 # update trust information
346 346 self._trustusers.update(self.configlist('trusted', 'users'))
347 347 self._trustgroups.update(self.configlist('trusted', 'groups'))
348 348
349 349 def backupconfig(self, section, item):
350 350 return (self._ocfg.backup(section, item),
351 351 self._tcfg.backup(section, item),
352 352 self._ucfg.backup(section, item),)
353 353 def restoreconfig(self, data):
354 354 self._ocfg.restore(data[0])
355 355 self._tcfg.restore(data[1])
356 356 self._ucfg.restore(data[2])
357 357
358 358 def setconfig(self, section, name, value, source=''):
359 359 for cfg in (self._ocfg, self._tcfg, self._ucfg):
360 360 cfg.set(section, name, value, source)
361 361 self.fixconfig(section=section)
362 362
363 363 def _data(self, untrusted):
364 364 return untrusted and self._ucfg or self._tcfg
365 365
366 366 def configsource(self, section, name, untrusted=False):
367 367 return self._data(untrusted).source(section, name)
368 368
369 369 def config(self, section, name, default=None, untrusted=False):
370 370 if isinstance(name, list):
371 371 alternates = name
372 372 else:
373 373 alternates = [name]
374 374
375 375 for n in alternates:
376 376 value = self._data(untrusted).get(section, n, None)
377 377 if value is not None:
378 378 name = n
379 379 break
380 380 else:
381 381 value = default
382 382
383 383 if self.debugflag and not untrusted and self._reportuntrusted:
384 384 for n in alternates:
385 385 uvalue = self._ucfg.get(section, n)
386 386 if uvalue is not None and uvalue != value:
387 387 self.debug("ignoring untrusted configuration option "
388 388 "%s.%s = %s\n" % (section, n, uvalue))
389 389 return value
390 390
391 391 def configsuboptions(self, section, name, default=None, untrusted=False):
392 392 """Get a config option and all sub-options.
393 393
394 394 Some config options have sub-options that are declared with the
395 395 format "key:opt = value". This method is used to return the main
396 396 option and all its declared sub-options.
397 397
398 398 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
399 399 is a dict of defined sub-options where keys and values are strings.
400 400 """
401 401 data = self._data(untrusted)
402 402 main = data.get(section, name, default)
403 403 if self.debugflag and not untrusted and self._reportuntrusted:
404 404 uvalue = self._ucfg.get(section, name)
405 405 if uvalue is not None and uvalue != main:
406 406 self.debug('ignoring untrusted configuration option '
407 407 '%s.%s = %s\n' % (section, name, uvalue))
408 408
409 409 sub = {}
410 410 prefix = '%s:' % name
411 411 for k, v in data.items(section):
412 412 if k.startswith(prefix):
413 413 sub[k[len(prefix):]] = v
414 414
415 415 if self.debugflag and not untrusted and self._reportuntrusted:
416 416 for k, v in sub.items():
417 417 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
418 418 if uvalue is not None and uvalue != v:
419 419 self.debug('ignoring untrusted configuration option '
420 420 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
421 421
422 422 return main, sub
423 423
424 424 def configpath(self, section, name, default=None, untrusted=False):
425 425 'get a path config item, expanded relative to repo root or config file'
426 426 v = self.config(section, name, default, untrusted)
427 427 if v is None:
428 428 return None
429 429 if not os.path.isabs(v) or "://" not in v:
430 430 src = self.configsource(section, name, untrusted)
431 431 if ':' in src:
432 432 base = os.path.dirname(src.rsplit(':')[0])
433 433 v = os.path.join(base, os.path.expanduser(v))
434 434 return v
435 435
436 436 def configbool(self, section, name, default=False, untrusted=False):
437 437 """parse a configuration element as a boolean
438 438
439 439 >>> u = ui(); s = 'foo'
440 440 >>> u.setconfig(s, 'true', 'yes')
441 441 >>> u.configbool(s, 'true')
442 442 True
443 443 >>> u.setconfig(s, 'false', 'no')
444 444 >>> u.configbool(s, 'false')
445 445 False
446 446 >>> u.configbool(s, 'unknown')
447 447 False
448 448 >>> u.configbool(s, 'unknown', True)
449 449 True
450 450 >>> u.setconfig(s, 'invalid', 'somevalue')
451 451 >>> u.configbool(s, 'invalid')
452 452 Traceback (most recent call last):
453 453 ...
454 454 ConfigError: foo.invalid is not a boolean ('somevalue')
455 455 """
456 456
457 457 v = self.config(section, name, None, untrusted)
458 458 if v is None:
459 459 return default
460 460 if isinstance(v, bool):
461 461 return v
462 462 b = util.parsebool(v)
463 463 if b is None:
464 464 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
465 465 % (section, name, v))
466 466 return b
467 467
468 468 def configwith(self, convert, section, name, default=None,
469 469 desc=None, untrusted=False):
470 470 """parse a configuration element with a conversion function
471 471
472 472 >>> u = ui(); s = 'foo'
473 473 >>> u.setconfig(s, 'float1', '42')
474 474 >>> u.configwith(float, s, 'float1')
475 475 42.0
476 476 >>> u.setconfig(s, 'float2', '-4.25')
477 477 >>> u.configwith(float, s, 'float2')
478 478 -4.25
479 479 >>> u.configwith(float, s, 'unknown', 7)
480 480 7
481 481 >>> u.setconfig(s, 'invalid', 'somevalue')
482 482 >>> u.configwith(float, s, 'invalid')
483 483 Traceback (most recent call last):
484 484 ...
485 485 ConfigError: foo.invalid is not a valid float ('somevalue')
486 486 >>> u.configwith(float, s, 'invalid', desc='womble')
487 487 Traceback (most recent call last):
488 488 ...
489 489 ConfigError: foo.invalid is not a valid womble ('somevalue')
490 490 """
491 491
492 492 v = self.config(section, name, None, untrusted)
493 493 if v is None:
494 494 return default
495 495 try:
496 496 return convert(v)
497 497 except ValueError:
498 498 if desc is None:
499 499 desc = convert.__name__
500 500 raise error.ConfigError(_("%s.%s is not a valid %s ('%s')")
501 501 % (section, name, desc, v))
502 502
503 503 def configint(self, section, name, default=None, untrusted=False):
504 504 """parse a configuration element as an integer
505 505
506 506 >>> u = ui(); s = 'foo'
507 507 >>> u.setconfig(s, 'int1', '42')
508 508 >>> u.configint(s, 'int1')
509 509 42
510 510 >>> u.setconfig(s, 'int2', '-42')
511 511 >>> u.configint(s, 'int2')
512 512 -42
513 513 >>> u.configint(s, 'unknown', 7)
514 514 7
515 515 >>> u.setconfig(s, 'invalid', 'somevalue')
516 516 >>> u.configint(s, 'invalid')
517 517 Traceback (most recent call last):
518 518 ...
519 519 ConfigError: foo.invalid is not a valid integer ('somevalue')
520 520 """
521 521
522 522 return self.configwith(int, section, name, default, 'integer',
523 523 untrusted)
524 524
525 525 def configbytes(self, section, name, default=0, untrusted=False):
526 526 """parse a configuration element as a quantity in bytes
527 527
528 528 Units can be specified as b (bytes), k or kb (kilobytes), m or
529 529 mb (megabytes), g or gb (gigabytes).
530 530
531 531 >>> u = ui(); s = 'foo'
532 532 >>> u.setconfig(s, 'val1', '42')
533 533 >>> u.configbytes(s, 'val1')
534 534 42
535 535 >>> u.setconfig(s, 'val2', '42.5 kb')
536 536 >>> u.configbytes(s, 'val2')
537 537 43520
538 538 >>> u.configbytes(s, 'unknown', '7 MB')
539 539 7340032
540 540 >>> u.setconfig(s, 'invalid', 'somevalue')
541 541 >>> u.configbytes(s, 'invalid')
542 542 Traceback (most recent call last):
543 543 ...
544 544 ConfigError: foo.invalid is not a byte quantity ('somevalue')
545 545 """
546 546
547 547 value = self.config(section, name, None, untrusted)
548 548 if value is None:
549 549 if not isinstance(default, str):
550 550 return default
551 551 value = default
552 552 try:
553 553 return util.sizetoint(value)
554 554 except error.ParseError:
555 555 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
556 556 % (section, name, value))
557 557
558 558 def configlist(self, section, name, default=None, untrusted=False):
559 559 """parse a configuration element as a list of comma/space separated
560 560 strings
561 561
562 562 >>> u = ui(); s = 'foo'
563 563 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
564 564 >>> u.configlist(s, 'list1')
565 565 ['this', 'is', 'a small', 'test']
566 566 """
567 567 # default is not always a list
568 568 if isinstance(default, bytes):
569 569 default = config.parselist(default)
570 570 return self.configwith(config.parselist, section, name, default or [],
571 571 'list', untrusted)
572 572
573 573 def hasconfig(self, section, name, untrusted=False):
574 574 return self._data(untrusted).hasitem(section, name)
575 575
576 576 def has_section(self, section, untrusted=False):
577 577 '''tell whether section exists in config.'''
578 578 return section in self._data(untrusted)
579 579
580 580 def configitems(self, section, untrusted=False, ignoresub=False):
581 581 items = self._data(untrusted).items(section)
582 582 if ignoresub:
583 583 newitems = {}
584 584 for k, v in items:
585 585 if ':' not in k:
586 586 newitems[k] = v
587 587 items = newitems.items()
588 588 if self.debugflag and not untrusted and self._reportuntrusted:
589 589 for k, v in self._ucfg.items(section):
590 590 if self._tcfg.get(section, k) != v:
591 591 self.debug("ignoring untrusted configuration option "
592 592 "%s.%s = %s\n" % (section, k, v))
593 593 return items
594 594
595 595 def walkconfig(self, untrusted=False):
596 596 cfg = self._data(untrusted)
597 597 for section in cfg.sections():
598 598 for name, value in self.configitems(section, untrusted):
599 599 yield section, name, value
600 600
601 601 def plain(self, feature=None):
602 602 '''is plain mode active?
603 603
604 604 Plain mode means that all configuration variables which affect
605 605 the behavior and output of Mercurial should be
606 606 ignored. Additionally, the output should be stable,
607 607 reproducible and suitable for use in scripts or applications.
608 608
609 609 The only way to trigger plain mode is by setting either the
610 610 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
611 611
612 612 The return value can either be
613 613 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
614 614 - True otherwise
615 615 '''
616 616 if ('HGPLAIN' not in encoding.environ and
617 617 'HGPLAINEXCEPT' not in encoding.environ):
618 618 return False
619 619 exceptions = encoding.environ.get('HGPLAINEXCEPT',
620 620 '').strip().split(',')
621 621 if feature and exceptions:
622 622 return feature not in exceptions
623 623 return True
624 624
625 625 def username(self):
626 626 """Return default username to be used in commits.
627 627
628 628 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
629 629 and stop searching if one of these is set.
630 630 If not found and ui.askusername is True, ask the user, else use
631 631 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
632 632 """
633 633 user = encoding.environ.get("HGUSER")
634 634 if user is None:
635 635 user = self.config("ui", ["username", "user"])
636 636 if user is not None:
637 637 user = os.path.expandvars(user)
638 638 if user is None:
639 639 user = encoding.environ.get("EMAIL")
640 640 if user is None and self.configbool("ui", "askusername"):
641 641 user = self.prompt(_("enter a commit username:"), default=None)
642 642 if user is None and not self.interactive():
643 643 try:
644 644 user = '%s@%s' % (util.getuser(), socket.getfqdn())
645 645 self.warn(_("no username found, using '%s' instead\n") % user)
646 646 except KeyError:
647 647 pass
648 648 if not user:
649 649 raise error.Abort(_('no username supplied'),
650 650 hint=_("use 'hg config --edit' "
651 651 'to set your username'))
652 652 if "\n" in user:
653 653 raise error.Abort(_("username %s contains a newline\n")
654 654 % repr(user))
655 655 return user
656 656
657 657 def shortuser(self, user):
658 658 """Return a short representation of a user name or email address."""
659 659 if not self.verbose:
660 660 user = util.shortuser(user)
661 661 return user
662 662
663 663 def expandpath(self, loc, default=None):
664 664 """Return repository location relative to cwd or from [paths]"""
665 665 try:
666 666 p = self.paths.getpath(loc)
667 667 if p:
668 668 return p.rawloc
669 669 except error.RepoError:
670 670 pass
671 671
672 672 if default:
673 673 try:
674 674 p = self.paths.getpath(default)
675 675 if p:
676 676 return p.rawloc
677 677 except error.RepoError:
678 678 pass
679 679
680 680 return loc
681 681
682 682 @util.propertycache
683 683 def paths(self):
684 684 return paths(self)
685 685
686 686 def pushbuffer(self, error=False, subproc=False, labeled=False):
687 687 """install a buffer to capture standard output of the ui object
688 688
689 689 If error is True, the error output will be captured too.
690 690
691 691 If subproc is True, output from subprocesses (typically hooks) will be
692 692 captured too.
693 693
694 694 If labeled is True, any labels associated with buffered
695 695 output will be handled. By default, this has no effect
696 696 on the output returned, but extensions and GUI tools may
697 697 handle this argument and returned styled output. If output
698 698 is being buffered so it can be captured and parsed or
699 699 processed, labeled should not be set to True.
700 700 """
701 701 self._buffers.append([])
702 702 self._bufferstates.append((error, subproc, labeled))
703 703 self._bufferapplylabels = labeled
704 704
705 705 def popbuffer(self):
706 706 '''pop the last buffer and return the buffered output'''
707 707 self._bufferstates.pop()
708 708 if self._bufferstates:
709 709 self._bufferapplylabels = self._bufferstates[-1][2]
710 710 else:
711 711 self._bufferapplylabels = None
712 712
713 713 return "".join(self._buffers.pop())
714 714
715 715 def write(self, *args, **opts):
716 716 '''write args to output
717 717
718 718 By default, this method simply writes to the buffer or stdout.
719 719 Color mode can be set on the UI class to have the output decorated
720 720 with color modifier before being written to stdout.
721 721
722 722 The color used is controlled by an optional keyword argument, "label".
723 723 This should be a string containing label names separated by space.
724 724 Label names take the form of "topic.type". For example, ui.debug()
725 725 issues a label of "ui.debug".
726 726
727 727 When labeling output for a specific command, a label of
728 728 "cmdname.type" is recommended. For example, status issues
729 729 a label of "status.modified" for modified files.
730 730 '''
731 731 if self._buffers and not opts.get('prompt', False):
732 732 if self._bufferapplylabels:
733 733 label = opts.get('label', '')
734 734 self._buffers[-1].extend(self.label(a, label) for a in args)
735 735 else:
736 736 self._buffers[-1].extend(args)
737 737 elif self._colormode == 'win32':
738 738 # windows color printing is its own can of crab, defer to
739 739 # the color module and that is it.
740 740 color.win32print(self, self._write, *args, **opts)
741 741 else:
742 742 msgs = args
743 743 if self._colormode is not None:
744 744 label = opts.get('label', '')
745 745 msgs = [self.label(a, label) for a in args]
746 746 self._write(*msgs, **opts)
747 747
748 748 def _write(self, *msgs, **opts):
749 749 self._progclear()
750 750 # opencode timeblockedsection because this is a critical path
751 751 starttime = util.timer()
752 752 try:
753 753 for a in msgs:
754 754 self.fout.write(a)
755 755 finally:
756 756 self._blockedtimes['stdio_blocked'] += \
757 757 (util.timer() - starttime) * 1000
758 758
759 759 def write_err(self, *args, **opts):
760 760 self._progclear()
761 761 if self._bufferstates and self._bufferstates[-1][0]:
762 762 self.write(*args, **opts)
763 763 elif self._colormode == 'win32':
764 764 # windows color printing is its own can of crab, defer to
765 765 # the color module and that is it.
766 766 color.win32print(self, self._write_err, *args, **opts)
767 767 else:
768 768 msgs = args
769 769 if self._colormode is not None:
770 770 label = opts.get('label', '')
771 771 msgs = [self.label(a, label) for a in args]
772 772 self._write_err(*msgs, **opts)
773 773
774 774 def _write_err(self, *msgs, **opts):
775 775 try:
776 776 with self.timeblockedsection('stdio'):
777 777 if not getattr(self.fout, 'closed', False):
778 778 self.fout.flush()
779 779 for a in msgs:
780 780 self.ferr.write(a)
781 781 # stderr may be buffered under win32 when redirected to files,
782 782 # including stdout.
783 783 if not getattr(self.ferr, 'closed', False):
784 784 self.ferr.flush()
785 785 except IOError as inst:
786 786 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
787 787 raise
788 788
789 789 def flush(self):
790 790 # opencode timeblockedsection because this is a critical path
791 791 starttime = util.timer()
792 792 try:
793 793 try: self.fout.flush()
794 794 except (IOError, ValueError): pass
795 795 try: self.ferr.flush()
796 796 except (IOError, ValueError): pass
797 797 finally:
798 798 self._blockedtimes['stdio_blocked'] += \
799 799 (util.timer() - starttime) * 1000
800 800
801 801 def _isatty(self, fh):
802 802 if self.configbool('ui', 'nontty', False):
803 803 return False
804 804 return util.isatty(fh)
805 805
806 806 def disablepager(self):
807 807 self._disablepager = True
808 808
809 809 def pager(self, command):
810 810 """Start a pager for subsequent command output.
811 811
812 812 Commands which produce a long stream of output should call
813 813 this function to activate the user's preferred pagination
814 814 mechanism (which may be no pager). Calling this function
815 815 precludes any future use of interactive functionality, such as
816 816 prompting the user or activating curses.
817 817
818 818 Args:
819 819 command: The full, non-aliased name of the command. That is, "log"
820 820 not "history, "summary" not "summ", etc.
821 821 """
822 822 if (self._disablepager
823 823 or self.pageractive
824 824 or command in self.configlist('pager', 'ignore')
825 825 or not self.configbool('pager', 'enable', True)
826 826 or not self.configbool('pager', 'attend-' + command, True)
827 827 # TODO: if we want to allow HGPLAINEXCEPT=pager,
828 828 # formatted() will need some adjustment.
829 829 or not self.formatted()
830 830 or self.plain()
831 831 # TODO: expose debugger-enabled on the UI object
832 832 or '--debugger' in pycompat.sysargv):
833 833 # We only want to paginate if the ui appears to be
834 834 # interactive, the user didn't say HGPLAIN or
835 835 # HGPLAINEXCEPT=pager, and the user didn't specify --debug.
836 836 return
837 837
838 838 # TODO: add a "system defaults" config section so this default
839 839 # of more(1) can be easily replaced with a global
840 840 # configuration file. For example, on OS X the sane default is
841 841 # less(1), not more(1), and on debian it's
842 842 # sensible-pager(1). We should probably also give the system
843 843 # default editor command similar treatment.
844 844 envpager = encoding.environ.get('PAGER', 'more')
845 845 pagercmd = self.config('pager', 'pager', envpager)
846 846 if not pagercmd:
847 847 return
848 848
849 if pycompat.osname == 'nt':
850 # `more` cannot be invoked with shell=False, but `more.com` can.
851 # Hide this implementation detail from the user, so we can also get
852 # sane bad PAGER behavior. If args are also given, the space in the
853 # command line forces shell=True, so that case doesn't need to be
854 # handled here.
855 if pagercmd == 'more':
856 pagercmd = 'more.com'
857
858 849 self.debug('starting pager for command %r\n' % command)
859 850 self.flush()
860 851 self.pageractive = True
861 852 # Preserve the formatted-ness of the UI. This is important
862 853 # because we mess with stdout, which might confuse
863 854 # auto-detection of things being formatted.
864 855 self.setconfig('ui', 'formatted', self.formatted(), 'pager')
865 856 self.setconfig('ui', 'interactive', False, 'pager')
866 857 if util.safehasattr(signal, "SIGPIPE"):
867 858 signal.signal(signal.SIGPIPE, _catchterm)
868 859 self._runpager(pagercmd)
869 860
870 861 def _runpager(self, command):
871 862 """Actually start the pager and set up file descriptors.
872 863
873 864 This is separate in part so that extensions (like chg) can
874 865 override how a pager is invoked.
875 866 """
876 867 if command == 'cat':
877 868 # Save ourselves some work.
878 869 return
879 870 # If the command doesn't contain any of these characters, we
880 871 # assume it's a binary and exec it directly. This means for
881 872 # simple pager command configurations, we can degrade
882 873 # gracefully and tell the user about their broken pager.
883 874 shell = any(c in command for c in "|&;<>()$`\\\"' \t\n*?[#~=%")
875
876 if pycompat.osname == 'nt' and not shell:
877 # Window's built-in `more` cannot be invoked with shell=False, but
878 # its `more.com` can. Hide this implementation detail from the
879 # user so we can also get sane bad PAGER behavior. MSYS has
880 # `more.exe`, so do a cmd.exe style resolution of the executable to
881 # determine which one to use.
882 fullcmd = util.findexe(command)
883 if not fullcmd:
884 self.warn(_("missing pager command '%s', skipping pager\n")
885 % command)
886 return
887
888 command = fullcmd
889
884 890 try:
885 891 pager = subprocess.Popen(
886 892 command, shell=shell, bufsize=-1,
887 893 close_fds=util.closefds, stdin=subprocess.PIPE,
888 894 stdout=util.stdout, stderr=util.stderr)
889 895 except OSError as e:
890 896 if e.errno == errno.ENOENT and not shell:
891 897 self.warn(_("missing pager command '%s', skipping pager\n")
892 898 % command)
893 899 return
894 900 raise
895 901
896 902 # back up original file descriptors
897 903 stdoutfd = os.dup(util.stdout.fileno())
898 904 stderrfd = os.dup(util.stderr.fileno())
899 905
900 906 os.dup2(pager.stdin.fileno(), util.stdout.fileno())
901 907 if self._isatty(util.stderr):
902 908 os.dup2(pager.stdin.fileno(), util.stderr.fileno())
903 909
904 910 @atexit.register
905 911 def killpager():
906 912 if util.safehasattr(signal, "SIGINT"):
907 913 signal.signal(signal.SIGINT, signal.SIG_IGN)
908 914 # restore original fds, closing pager.stdin copies in the process
909 915 os.dup2(stdoutfd, util.stdout.fileno())
910 916 os.dup2(stderrfd, util.stderr.fileno())
911 917 pager.stdin.close()
912 918 pager.wait()
913 919
914 920 def interface(self, feature):
915 921 """what interface to use for interactive console features?
916 922
917 923 The interface is controlled by the value of `ui.interface` but also by
918 924 the value of feature-specific configuration. For example:
919 925
920 926 ui.interface.histedit = text
921 927 ui.interface.chunkselector = curses
922 928
923 929 Here the features are "histedit" and "chunkselector".
924 930
925 931 The configuration above means that the default interfaces for commands
926 932 is curses, the interface for histedit is text and the interface for
927 933 selecting chunk is crecord (the best curses interface available).
928 934
929 935 Consider the following example:
930 936 ui.interface = curses
931 937 ui.interface.histedit = text
932 938
933 939 Then histedit will use the text interface and chunkselector will use
934 940 the default curses interface (crecord at the moment).
935 941 """
936 942 alldefaults = frozenset(["text", "curses"])
937 943
938 944 featureinterfaces = {
939 945 "chunkselector": [
940 946 "text",
941 947 "curses",
942 948 ]
943 949 }
944 950
945 951 # Feature-specific interface
946 952 if feature not in featureinterfaces.keys():
947 953 # Programming error, not user error
948 954 raise ValueError("Unknown feature requested %s" % feature)
949 955
950 956 availableinterfaces = frozenset(featureinterfaces[feature])
951 957 if alldefaults > availableinterfaces:
952 958 # Programming error, not user error. We need a use case to
953 959 # define the right thing to do here.
954 960 raise ValueError(
955 961 "Feature %s does not handle all default interfaces" %
956 962 feature)
957 963
958 964 if self.plain():
959 965 return "text"
960 966
961 967 # Default interface for all the features
962 968 defaultinterface = "text"
963 969 i = self.config("ui", "interface", None)
964 970 if i in alldefaults:
965 971 defaultinterface = i
966 972
967 973 choseninterface = defaultinterface
968 974 f = self.config("ui", "interface.%s" % feature, None)
969 975 if f in availableinterfaces:
970 976 choseninterface = f
971 977
972 978 if i is not None and defaultinterface != i:
973 979 if f is not None:
974 980 self.warn(_("invalid value for ui.interface: %s\n") %
975 981 (i,))
976 982 else:
977 983 self.warn(_("invalid value for ui.interface: %s (using %s)\n") %
978 984 (i, choseninterface))
979 985 if f is not None and choseninterface != f:
980 986 self.warn(_("invalid value for ui.interface.%s: %s (using %s)\n") %
981 987 (feature, f, choseninterface))
982 988
983 989 return choseninterface
984 990
985 991 def interactive(self):
986 992 '''is interactive input allowed?
987 993
988 994 An interactive session is a session where input can be reasonably read
989 995 from `sys.stdin'. If this function returns false, any attempt to read
990 996 from stdin should fail with an error, unless a sensible default has been
991 997 specified.
992 998
993 999 Interactiveness is triggered by the value of the `ui.interactive'
994 1000 configuration variable or - if it is unset - when `sys.stdin' points
995 1001 to a terminal device.
996 1002
997 1003 This function refers to input only; for output, see `ui.formatted()'.
998 1004 '''
999 1005 i = self.configbool("ui", "interactive", None)
1000 1006 if i is None:
1001 1007 # some environments replace stdin without implementing isatty
1002 1008 # usually those are non-interactive
1003 1009 return self._isatty(self.fin)
1004 1010
1005 1011 return i
1006 1012
1007 1013 def termwidth(self):
1008 1014 '''how wide is the terminal in columns?
1009 1015 '''
1010 1016 if 'COLUMNS' in encoding.environ:
1011 1017 try:
1012 1018 return int(encoding.environ['COLUMNS'])
1013 1019 except ValueError:
1014 1020 pass
1015 1021 return scmutil.termsize(self)[0]
1016 1022
1017 1023 def formatted(self):
1018 1024 '''should formatted output be used?
1019 1025
1020 1026 It is often desirable to format the output to suite the output medium.
1021 1027 Examples of this are truncating long lines or colorizing messages.
1022 1028 However, this is not often not desirable when piping output into other
1023 1029 utilities, e.g. `grep'.
1024 1030
1025 1031 Formatted output is triggered by the value of the `ui.formatted'
1026 1032 configuration variable or - if it is unset - when `sys.stdout' points
1027 1033 to a terminal device. Please note that `ui.formatted' should be
1028 1034 considered an implementation detail; it is not intended for use outside
1029 1035 Mercurial or its extensions.
1030 1036
1031 1037 This function refers to output only; for input, see `ui.interactive()'.
1032 1038 This function always returns false when in plain mode, see `ui.plain()'.
1033 1039 '''
1034 1040 if self.plain():
1035 1041 return False
1036 1042
1037 1043 i = self.configbool("ui", "formatted", None)
1038 1044 if i is None:
1039 1045 # some environments replace stdout without implementing isatty
1040 1046 # usually those are non-interactive
1041 1047 return self._isatty(self.fout)
1042 1048
1043 1049 return i
1044 1050
1045 1051 def _readline(self, prompt=''):
1046 1052 if self._isatty(self.fin):
1047 1053 try:
1048 1054 # magically add command line editing support, where
1049 1055 # available
1050 1056 import readline
1051 1057 # force demandimport to really load the module
1052 1058 readline.read_history_file
1053 1059 # windows sometimes raises something other than ImportError
1054 1060 except Exception:
1055 1061 pass
1056 1062
1057 1063 # call write() so output goes through subclassed implementation
1058 1064 # e.g. color extension on Windows
1059 1065 self.write(prompt, prompt=True)
1060 1066
1061 1067 # instead of trying to emulate raw_input, swap (self.fin,
1062 1068 # self.fout) with (sys.stdin, sys.stdout)
1063 1069 oldin = sys.stdin
1064 1070 oldout = sys.stdout
1065 1071 sys.stdin = self.fin
1066 1072 sys.stdout = self.fout
1067 1073 # prompt ' ' must exist; otherwise readline may delete entire line
1068 1074 # - http://bugs.python.org/issue12833
1069 1075 with self.timeblockedsection('stdio'):
1070 1076 line = raw_input(' ')
1071 1077 sys.stdin = oldin
1072 1078 sys.stdout = oldout
1073 1079
1074 1080 # When stdin is in binary mode on Windows, it can cause
1075 1081 # raw_input() to emit an extra trailing carriage return
1076 1082 if os.linesep == '\r\n' and line and line[-1] == '\r':
1077 1083 line = line[:-1]
1078 1084 return line
1079 1085
1080 1086 def prompt(self, msg, default="y"):
1081 1087 """Prompt user with msg, read response.
1082 1088 If ui is not interactive, the default is returned.
1083 1089 """
1084 1090 if not self.interactive():
1085 1091 self.write(msg, ' ', default or '', "\n")
1086 1092 return default
1087 1093 try:
1088 1094 r = self._readline(self.label(msg, 'ui.prompt'))
1089 1095 if not r:
1090 1096 r = default
1091 1097 if self.configbool('ui', 'promptecho'):
1092 1098 self.write(r, "\n")
1093 1099 return r
1094 1100 except EOFError:
1095 1101 raise error.ResponseExpected()
1096 1102
1097 1103 @staticmethod
1098 1104 def extractchoices(prompt):
1099 1105 """Extract prompt message and list of choices from specified prompt.
1100 1106
1101 1107 This returns tuple "(message, choices)", and "choices" is the
1102 1108 list of tuple "(response character, text without &)".
1103 1109
1104 1110 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
1105 1111 ('awake? ', [('y', 'Yes'), ('n', 'No')])
1106 1112 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
1107 1113 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
1108 1114 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
1109 1115 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
1110 1116 """
1111 1117
1112 1118 # Sadly, the prompt string may have been built with a filename
1113 1119 # containing "$$" so let's try to find the first valid-looking
1114 1120 # prompt to start parsing. Sadly, we also can't rely on
1115 1121 # choices containing spaces, ASCII, or basically anything
1116 1122 # except an ampersand followed by a character.
1117 1123 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
1118 1124 msg = m.group(1)
1119 1125 choices = [p.strip(' ') for p in m.group(2).split('$$')]
1120 1126 return (msg,
1121 1127 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
1122 1128 for s in choices])
1123 1129
1124 1130 def promptchoice(self, prompt, default=0):
1125 1131 """Prompt user with a message, read response, and ensure it matches
1126 1132 one of the provided choices. The prompt is formatted as follows:
1127 1133
1128 1134 "would you like fries with that (Yn)? $$ &Yes $$ &No"
1129 1135
1130 1136 The index of the choice is returned. Responses are case
1131 1137 insensitive. If ui is not interactive, the default is
1132 1138 returned.
1133 1139 """
1134 1140
1135 1141 msg, choices = self.extractchoices(prompt)
1136 1142 resps = [r for r, t in choices]
1137 1143 while True:
1138 1144 r = self.prompt(msg, resps[default])
1139 1145 if r.lower() in resps:
1140 1146 return resps.index(r.lower())
1141 1147 self.write(_("unrecognized response\n"))
1142 1148
1143 1149 def getpass(self, prompt=None, default=None):
1144 1150 if not self.interactive():
1145 1151 return default
1146 1152 try:
1147 1153 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
1148 1154 # disable getpass() only if explicitly specified. it's still valid
1149 1155 # to interact with tty even if fin is not a tty.
1150 1156 with self.timeblockedsection('stdio'):
1151 1157 if self.configbool('ui', 'nontty'):
1152 1158 l = self.fin.readline()
1153 1159 if not l:
1154 1160 raise EOFError
1155 1161 return l.rstrip('\n')
1156 1162 else:
1157 1163 return getpass.getpass('')
1158 1164 except EOFError:
1159 1165 raise error.ResponseExpected()
1160 1166 def status(self, *msg, **opts):
1161 1167 '''write status message to output (if ui.quiet is False)
1162 1168
1163 1169 This adds an output label of "ui.status".
1164 1170 '''
1165 1171 if not self.quiet:
1166 1172 opts[r'label'] = opts.get(r'label', '') + ' ui.status'
1167 1173 self.write(*msg, **opts)
1168 1174 def warn(self, *msg, **opts):
1169 1175 '''write warning message to output (stderr)
1170 1176
1171 1177 This adds an output label of "ui.warning".
1172 1178 '''
1173 1179 opts[r'label'] = opts.get(r'label', '') + ' ui.warning'
1174 1180 self.write_err(*msg, **opts)
1175 1181 def note(self, *msg, **opts):
1176 1182 '''write note to output (if ui.verbose is True)
1177 1183
1178 1184 This adds an output label of "ui.note".
1179 1185 '''
1180 1186 if self.verbose:
1181 1187 opts[r'label'] = opts.get(r'label', '') + ' ui.note'
1182 1188 self.write(*msg, **opts)
1183 1189 def debug(self, *msg, **opts):
1184 1190 '''write debug message to output (if ui.debugflag is True)
1185 1191
1186 1192 This adds an output label of "ui.debug".
1187 1193 '''
1188 1194 if self.debugflag:
1189 1195 opts[r'label'] = opts.get(r'label', '') + ' ui.debug'
1190 1196 self.write(*msg, **opts)
1191 1197
1192 1198 def edit(self, text, user, extra=None, editform=None, pending=None,
1193 1199 repopath=None):
1194 1200 extra_defaults = {
1195 1201 'prefix': 'editor',
1196 1202 'suffix': '.txt',
1197 1203 }
1198 1204 if extra is not None:
1199 1205 extra_defaults.update(extra)
1200 1206 extra = extra_defaults
1201 1207
1202 1208 rdir = None
1203 1209 if self.configbool('experimental', 'editortmpinhg'):
1204 1210 rdir = repopath
1205 1211 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
1206 1212 suffix=extra['suffix'], text=True,
1207 1213 dir=rdir)
1208 1214 try:
1209 1215 f = os.fdopen(fd, pycompat.sysstr("w"))
1210 1216 f.write(encoding.strfromlocal(text))
1211 1217 f.close()
1212 1218
1213 1219 environ = {'HGUSER': user}
1214 1220 if 'transplant_source' in extra:
1215 1221 environ.update({'HGREVISION': hex(extra['transplant_source'])})
1216 1222 for label in ('intermediate-source', 'source', 'rebase_source'):
1217 1223 if label in extra:
1218 1224 environ.update({'HGREVISION': extra[label]})
1219 1225 break
1220 1226 if editform:
1221 1227 environ.update({'HGEDITFORM': editform})
1222 1228 if pending:
1223 1229 environ.update({'HG_PENDING': pending})
1224 1230
1225 1231 editor = self.geteditor()
1226 1232
1227 1233 self.system("%s \"%s\"" % (editor, name),
1228 1234 environ=environ,
1229 1235 onerr=error.Abort, errprefix=_("edit failed"),
1230 1236 blockedtag='editor')
1231 1237
1232 1238 f = open(name)
1233 1239 t = encoding.strtolocal(f.read())
1234 1240 f.close()
1235 1241 finally:
1236 1242 os.unlink(name)
1237 1243
1238 1244 return t
1239 1245
1240 1246 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None,
1241 1247 blockedtag=None):
1242 1248 '''execute shell command with appropriate output stream. command
1243 1249 output will be redirected if fout is not stdout.
1244 1250
1245 1251 if command fails and onerr is None, return status, else raise onerr
1246 1252 object as exception.
1247 1253 '''
1248 1254 if blockedtag is None:
1249 1255 # Long cmds tend to be because of an absolute path on cmd. Keep
1250 1256 # the tail end instead
1251 1257 cmdsuffix = cmd.translate(None, _keepalnum)[-85:]
1252 1258 blockedtag = 'unknown_system_' + cmdsuffix
1253 1259 out = self.fout
1254 1260 if any(s[1] for s in self._bufferstates):
1255 1261 out = self
1256 1262 with self.timeblockedsection(blockedtag):
1257 1263 rc = self._runsystem(cmd, environ=environ, cwd=cwd, out=out)
1258 1264 if rc and onerr:
1259 1265 errmsg = '%s %s' % (os.path.basename(cmd.split(None, 1)[0]),
1260 1266 util.explainexit(rc)[0])
1261 1267 if errprefix:
1262 1268 errmsg = '%s: %s' % (errprefix, errmsg)
1263 1269 raise onerr(errmsg)
1264 1270 return rc
1265 1271
1266 1272 def _runsystem(self, cmd, environ, cwd, out):
1267 1273 """actually execute the given shell command (can be overridden by
1268 1274 extensions like chg)"""
1269 1275 return util.system(cmd, environ=environ, cwd=cwd, out=out)
1270 1276
1271 1277 def traceback(self, exc=None, force=False):
1272 1278 '''print exception traceback if traceback printing enabled or forced.
1273 1279 only to call in exception handler. returns true if traceback
1274 1280 printed.'''
1275 1281 if self.tracebackflag or force:
1276 1282 if exc is None:
1277 1283 exc = sys.exc_info()
1278 1284 cause = getattr(exc[1], 'cause', None)
1279 1285
1280 1286 if cause is not None:
1281 1287 causetb = traceback.format_tb(cause[2])
1282 1288 exctb = traceback.format_tb(exc[2])
1283 1289 exconly = traceback.format_exception_only(cause[0], cause[1])
1284 1290
1285 1291 # exclude frame where 'exc' was chained and rethrown from exctb
1286 1292 self.write_err('Traceback (most recent call last):\n',
1287 1293 ''.join(exctb[:-1]),
1288 1294 ''.join(causetb),
1289 1295 ''.join(exconly))
1290 1296 else:
1291 1297 output = traceback.format_exception(exc[0], exc[1], exc[2])
1292 1298 data = r''.join(output)
1293 1299 if pycompat.ispy3:
1294 1300 enc = pycompat.sysstr(encoding.encoding)
1295 1301 data = data.encode(enc, errors=r'replace')
1296 1302 self.write_err(data)
1297 1303 return self.tracebackflag or force
1298 1304
1299 1305 def geteditor(self):
1300 1306 '''return editor to use'''
1301 1307 if pycompat.sysplatform == 'plan9':
1302 1308 # vi is the MIPS instruction simulator on Plan 9. We
1303 1309 # instead default to E to plumb commit messages to
1304 1310 # avoid confusion.
1305 1311 editor = 'E'
1306 1312 else:
1307 1313 editor = 'vi'
1308 1314 return (encoding.environ.get("HGEDITOR") or
1309 1315 self.config("ui", "editor") or
1310 1316 encoding.environ.get("VISUAL") or
1311 1317 encoding.environ.get("EDITOR", editor))
1312 1318
1313 1319 @util.propertycache
1314 1320 def _progbar(self):
1315 1321 """setup the progbar singleton to the ui object"""
1316 1322 if (self.quiet or self.debugflag
1317 1323 or self.configbool('progress', 'disable', False)
1318 1324 or not progress.shouldprint(self)):
1319 1325 return None
1320 1326 return getprogbar(self)
1321 1327
1322 1328 def _progclear(self):
1323 1329 """clear progress bar output if any. use it before any output"""
1324 1330 if '_progbar' not in vars(self): # nothing loaded yet
1325 1331 return
1326 1332 if self._progbar is not None and self._progbar.printed:
1327 1333 self._progbar.clear()
1328 1334
1329 1335 def progress(self, topic, pos, item="", unit="", total=None):
1330 1336 '''show a progress message
1331 1337
1332 1338 By default a textual progress bar will be displayed if an operation
1333 1339 takes too long. 'topic' is the current operation, 'item' is a
1334 1340 non-numeric marker of the current position (i.e. the currently
1335 1341 in-process file), 'pos' is the current numeric position (i.e.
1336 1342 revision, bytes, etc.), unit is a corresponding unit label,
1337 1343 and total is the highest expected pos.
1338 1344
1339 1345 Multiple nested topics may be active at a time.
1340 1346
1341 1347 All topics should be marked closed by setting pos to None at
1342 1348 termination.
1343 1349 '''
1344 1350 if self._progbar is not None:
1345 1351 self._progbar.progress(topic, pos, item=item, unit=unit,
1346 1352 total=total)
1347 1353 if pos is None or not self.configbool('progress', 'debug'):
1348 1354 return
1349 1355
1350 1356 if unit:
1351 1357 unit = ' ' + unit
1352 1358 if item:
1353 1359 item = ' ' + item
1354 1360
1355 1361 if total:
1356 1362 pct = 100.0 * pos / total
1357 1363 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1358 1364 % (topic, item, pos, total, unit, pct))
1359 1365 else:
1360 1366 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1361 1367
1362 1368 def log(self, service, *msg, **opts):
1363 1369 '''hook for logging facility extensions
1364 1370
1365 1371 service should be a readily-identifiable subsystem, which will
1366 1372 allow filtering.
1367 1373
1368 1374 *msg should be a newline-terminated format string to log, and
1369 1375 then any values to %-format into that format string.
1370 1376
1371 1377 **opts currently has no defined meanings.
1372 1378 '''
1373 1379
1374 1380 def label(self, msg, label):
1375 1381 '''style msg based on supplied label
1376 1382
1377 1383 If some color mode is enabled, this will add the necessary control
1378 1384 characters to apply such color. In addition, 'debug' color mode adds
1379 1385 markup showing which label affects a piece of text.
1380 1386
1381 1387 ui.write(s, 'label') is equivalent to
1382 1388 ui.write(ui.label(s, 'label')).
1383 1389 '''
1384 1390 if self._colormode is not None:
1385 1391 return color.colorlabel(self, msg, label)
1386 1392 return msg
1387 1393
1388 1394 def develwarn(self, msg, stacklevel=1, config=None):
1389 1395 """issue a developer warning message
1390 1396
1391 1397 Use 'stacklevel' to report the offender some layers further up in the
1392 1398 stack.
1393 1399 """
1394 1400 if not self.configbool('devel', 'all-warnings'):
1395 1401 if config is not None and not self.configbool('devel', config):
1396 1402 return
1397 1403 msg = 'devel-warn: ' + msg
1398 1404 stacklevel += 1 # get in develwarn
1399 1405 if self.tracebackflag:
1400 1406 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1401 1407 self.log('develwarn', '%s at:\n%s' %
1402 1408 (msg, ''.join(util.getstackframes(stacklevel))))
1403 1409 else:
1404 1410 curframe = inspect.currentframe()
1405 1411 calframe = inspect.getouterframes(curframe, 2)
1406 1412 self.write_err('%s at: %s:%s (%s)\n'
1407 1413 % ((msg,) + calframe[stacklevel][1:4]))
1408 1414 self.log('develwarn', '%s at: %s:%s (%s)\n',
1409 1415 msg, *calframe[stacklevel][1:4])
1410 1416 curframe = calframe = None # avoid cycles
1411 1417
1412 1418 def deprecwarn(self, msg, version):
1413 1419 """issue a deprecation warning
1414 1420
1415 1421 - msg: message explaining what is deprecated and how to upgrade,
1416 1422 - version: last version where the API will be supported,
1417 1423 """
1418 1424 if not (self.configbool('devel', 'all-warnings')
1419 1425 or self.configbool('devel', 'deprec-warn')):
1420 1426 return
1421 1427 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1422 1428 " update your code.)") % version
1423 1429 self.develwarn(msg, stacklevel=2, config='deprec-warn')
1424 1430
1425 1431 def exportableenviron(self):
1426 1432 """The environment variables that are safe to export, e.g. through
1427 1433 hgweb.
1428 1434 """
1429 1435 return self._exportableenviron
1430 1436
1431 1437 @contextlib.contextmanager
1432 1438 def configoverride(self, overrides, source=""):
1433 1439 """Context manager for temporary config overrides
1434 1440 `overrides` must be a dict of the following structure:
1435 1441 {(section, name) : value}"""
1436 1442 backups = {}
1437 1443 try:
1438 1444 for (section, name), value in overrides.items():
1439 1445 backups[(section, name)] = self.backupconfig(section, name)
1440 1446 self.setconfig(section, name, value, source)
1441 1447 yield
1442 1448 finally:
1443 1449 for __, backup in backups.items():
1444 1450 self.restoreconfig(backup)
1445 1451 # just restoring ui.quiet config to the previous value is not enough
1446 1452 # as it does not update ui.quiet class member
1447 1453 if ('ui', 'quiet') in overrides:
1448 1454 self.fixconfig(section='ui')
1449 1455
1450 1456 class paths(dict):
1451 1457 """Represents a collection of paths and their configs.
1452 1458
1453 1459 Data is initially derived from ui instances and the config files they have
1454 1460 loaded.
1455 1461 """
1456 1462 def __init__(self, ui):
1457 1463 dict.__init__(self)
1458 1464
1459 1465 for name, loc in ui.configitems('paths', ignoresub=True):
1460 1466 # No location is the same as not existing.
1461 1467 if not loc:
1462 1468 continue
1463 1469 loc, sub = ui.configsuboptions('paths', name)
1464 1470 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1465 1471
1466 1472 def getpath(self, name, default=None):
1467 1473 """Return a ``path`` from a string, falling back to default.
1468 1474
1469 1475 ``name`` can be a named path or locations. Locations are filesystem
1470 1476 paths or URIs.
1471 1477
1472 1478 Returns None if ``name`` is not a registered path, a URI, or a local
1473 1479 path to a repo.
1474 1480 """
1475 1481 # Only fall back to default if no path was requested.
1476 1482 if name is None:
1477 1483 if not default:
1478 1484 default = ()
1479 1485 elif not isinstance(default, (tuple, list)):
1480 1486 default = (default,)
1481 1487 for k in default:
1482 1488 try:
1483 1489 return self[k]
1484 1490 except KeyError:
1485 1491 continue
1486 1492 return None
1487 1493
1488 1494 # Most likely empty string.
1489 1495 # This may need to raise in the future.
1490 1496 if not name:
1491 1497 return None
1492 1498
1493 1499 try:
1494 1500 return self[name]
1495 1501 except KeyError:
1496 1502 # Try to resolve as a local path or URI.
1497 1503 try:
1498 1504 # We don't pass sub-options in, so no need to pass ui instance.
1499 1505 return path(None, None, rawloc=name)
1500 1506 except ValueError:
1501 1507 raise error.RepoError(_('repository %s does not exist') %
1502 1508 name)
1503 1509
1504 1510 _pathsuboptions = {}
1505 1511
1506 1512 def pathsuboption(option, attr):
1507 1513 """Decorator used to declare a path sub-option.
1508 1514
1509 1515 Arguments are the sub-option name and the attribute it should set on
1510 1516 ``path`` instances.
1511 1517
1512 1518 The decorated function will receive as arguments a ``ui`` instance,
1513 1519 ``path`` instance, and the string value of this option from the config.
1514 1520 The function should return the value that will be set on the ``path``
1515 1521 instance.
1516 1522
1517 1523 This decorator can be used to perform additional verification of
1518 1524 sub-options and to change the type of sub-options.
1519 1525 """
1520 1526 def register(func):
1521 1527 _pathsuboptions[option] = (attr, func)
1522 1528 return func
1523 1529 return register
1524 1530
1525 1531 @pathsuboption('pushurl', 'pushloc')
1526 1532 def pushurlpathoption(ui, path, value):
1527 1533 u = util.url(value)
1528 1534 # Actually require a URL.
1529 1535 if not u.scheme:
1530 1536 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1531 1537 return None
1532 1538
1533 1539 # Don't support the #foo syntax in the push URL to declare branch to
1534 1540 # push.
1535 1541 if u.fragment:
1536 1542 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1537 1543 'ignoring)\n') % path.name)
1538 1544 u.fragment = None
1539 1545
1540 1546 return str(u)
1541 1547
1542 1548 @pathsuboption('pushrev', 'pushrev')
1543 1549 def pushrevpathoption(ui, path, value):
1544 1550 return value
1545 1551
1546 1552 class path(object):
1547 1553 """Represents an individual path and its configuration."""
1548 1554
1549 1555 def __init__(self, ui, name, rawloc=None, suboptions=None):
1550 1556 """Construct a path from its config options.
1551 1557
1552 1558 ``ui`` is the ``ui`` instance the path is coming from.
1553 1559 ``name`` is the symbolic name of the path.
1554 1560 ``rawloc`` is the raw location, as defined in the config.
1555 1561 ``pushloc`` is the raw locations pushes should be made to.
1556 1562
1557 1563 If ``name`` is not defined, we require that the location be a) a local
1558 1564 filesystem path with a .hg directory or b) a URL. If not,
1559 1565 ``ValueError`` is raised.
1560 1566 """
1561 1567 if not rawloc:
1562 1568 raise ValueError('rawloc must be defined')
1563 1569
1564 1570 # Locations may define branches via syntax <base>#<branch>.
1565 1571 u = util.url(rawloc)
1566 1572 branch = None
1567 1573 if u.fragment:
1568 1574 branch = u.fragment
1569 1575 u.fragment = None
1570 1576
1571 1577 self.url = u
1572 1578 self.branch = branch
1573 1579
1574 1580 self.name = name
1575 1581 self.rawloc = rawloc
1576 1582 self.loc = '%s' % u
1577 1583
1578 1584 # When given a raw location but not a symbolic name, validate the
1579 1585 # location is valid.
1580 1586 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1581 1587 raise ValueError('location is not a URL or path to a local '
1582 1588 'repo: %s' % rawloc)
1583 1589
1584 1590 suboptions = suboptions or {}
1585 1591
1586 1592 # Now process the sub-options. If a sub-option is registered, its
1587 1593 # attribute will always be present. The value will be None if there
1588 1594 # was no valid sub-option.
1589 1595 for suboption, (attr, func) in _pathsuboptions.iteritems():
1590 1596 if suboption not in suboptions:
1591 1597 setattr(self, attr, None)
1592 1598 continue
1593 1599
1594 1600 value = func(ui, self, suboptions[suboption])
1595 1601 setattr(self, attr, value)
1596 1602
1597 1603 def _isvalidlocalpath(self, path):
1598 1604 """Returns True if the given path is a potentially valid repository.
1599 1605 This is its own function so that extensions can change the definition of
1600 1606 'valid' in this case (like when pulling from a git repo into a hg
1601 1607 one)."""
1602 1608 return os.path.isdir(os.path.join(path, '.hg'))
1603 1609
1604 1610 @property
1605 1611 def suboptions(self):
1606 1612 """Return sub-options and their values for this path.
1607 1613
1608 1614 This is intended to be used for presentation purposes.
1609 1615 """
1610 1616 d = {}
1611 1617 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1612 1618 value = getattr(self, attr)
1613 1619 if value is not None:
1614 1620 d[subopt] = value
1615 1621 return d
1616 1622
1617 1623 # we instantiate one globally shared progress bar to avoid
1618 1624 # competing progress bars when multiple UI objects get created
1619 1625 _progresssingleton = None
1620 1626
1621 1627 def getprogbar(ui):
1622 1628 global _progresssingleton
1623 1629 if _progresssingleton is None:
1624 1630 # passing 'ui' object to the singleton is fishy,
1625 1631 # this is how the extension used to work but feel free to rework it.
1626 1632 _progresssingleton = progress.progbar(ui)
1627 1633 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now