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