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