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