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