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