##// END OF EJS Templates
ui: fix check-code error
Henrik Stuart -
r11036:4efdccac default
parent child Browse files
Show More
@@ -1,547 +1,548 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 i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, util, error
10 import config, util, error
11
11
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
12 _booleans = {'1': True, 'yes': True, 'true': True, 'on': True,
13 '0': False, 'no': False, 'false': False, 'off': False}
13 '0': False, 'no': False, 'false': False, 'off': False}
14
14
15 class ui(object):
15 class ui(object):
16 def __init__(self, src=None):
16 def __init__(self, src=None):
17 self._buffers = []
17 self._buffers = []
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
18 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
19 self._reportuntrusted = True
19 self._reportuntrusted = True
20 self._ocfg = config.config() # overlay
20 self._ocfg = config.config() # overlay
21 self._tcfg = config.config() # trusted
21 self._tcfg = config.config() # trusted
22 self._ucfg = config.config() # untrusted
22 self._ucfg = config.config() # untrusted
23 self._trustusers = set()
23 self._trustusers = set()
24 self._trustgroups = set()
24 self._trustgroups = set()
25
25
26 if src:
26 if src:
27 self._tcfg = src._tcfg.copy()
27 self._tcfg = src._tcfg.copy()
28 self._ucfg = src._ucfg.copy()
28 self._ucfg = src._ucfg.copy()
29 self._ocfg = src._ocfg.copy()
29 self._ocfg = src._ocfg.copy()
30 self._trustusers = src._trustusers.copy()
30 self._trustusers = src._trustusers.copy()
31 self._trustgroups = src._trustgroups.copy()
31 self._trustgroups = src._trustgroups.copy()
32 self.environ = src.environ
32 self.environ = src.environ
33 self.fixconfig()
33 self.fixconfig()
34 else:
34 else:
35 # shared read-only environment
35 # shared read-only environment
36 self.environ = os.environ
36 self.environ = os.environ
37 # we always trust global config files
37 # we always trust global config files
38 for f in util.rcpath():
38 for f in util.rcpath():
39 self.readconfig(f, trust=True)
39 self.readconfig(f, trust=True)
40
40
41 def copy(self):
41 def copy(self):
42 return self.__class__(self)
42 return self.__class__(self)
43
43
44 def _is_trusted(self, fp, f):
44 def _is_trusted(self, fp, f):
45 st = util.fstat(fp)
45 st = util.fstat(fp)
46 if util.isowner(st):
46 if util.isowner(st):
47 return True
47 return True
48
48
49 tusers, tgroups = self._trustusers, self._trustgroups
49 tusers, tgroups = self._trustusers, self._trustgroups
50 if '*' in tusers or '*' in tgroups:
50 if '*' in tusers or '*' in tgroups:
51 return True
51 return True
52
52
53 user = util.username(st.st_uid)
53 user = util.username(st.st_uid)
54 group = util.groupname(st.st_gid)
54 group = util.groupname(st.st_gid)
55 if user in tusers or group in tgroups or user == util.username():
55 if user in tusers or group in tgroups or user == util.username():
56 return True
56 return True
57
57
58 if self._reportuntrusted:
58 if self._reportuntrusted:
59 self.warn(_('Not trusting file %s from untrusted '
59 self.warn(_('Not trusting file %s from untrusted '
60 'user %s, group %s\n') % (f, user, group))
60 'user %s, group %s\n') % (f, user, group))
61 return False
61 return False
62
62
63 def readconfig(self, filename, root=None, trust=False,
63 def readconfig(self, filename, root=None, trust=False,
64 sections=None, remap=None):
64 sections=None, remap=None):
65 try:
65 try:
66 fp = open(filename)
66 fp = open(filename)
67 except IOError:
67 except IOError:
68 if not sections: # ignore unless we were looking for something
68 if not sections: # ignore unless we were looking for something
69 return
69 return
70 raise
70 raise
71
71
72 cfg = config.config()
72 cfg = config.config()
73 trusted = sections or trust or self._is_trusted(fp, filename)
73 trusted = sections or trust or self._is_trusted(fp, filename)
74
74
75 try:
75 try:
76 cfg.read(filename, fp, sections=sections, remap=remap)
76 cfg.read(filename, fp, sections=sections, remap=remap)
77 except error.ConfigError, inst:
77 except error.ConfigError, inst:
78 if trusted:
78 if trusted:
79 raise
79 raise
80 self.warn(_("Ignored: %s\n") % str(inst))
80 self.warn(_("Ignored: %s\n") % str(inst))
81
81
82 if self.plain():
82 if self.plain():
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
83 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
84 'logtemplate', 'style',
84 'logtemplate', 'style',
85 'traceback', 'verbose'):
85 'traceback', 'verbose'):
86 if k in cfg['ui']:
86 if k in cfg['ui']:
87 del cfg['ui'][k]
87 del cfg['ui'][k]
88 for k, v in cfg.items('alias'):
88 for k, v in cfg.items('alias'):
89 del cfg['alias'][k]
89 del cfg['alias'][k]
90 for k, v in cfg.items('defaults'):
90 for k, v in cfg.items('defaults'):
91 del cfg['defaults'][k]
91 del cfg['defaults'][k]
92
92
93 if trusted:
93 if trusted:
94 self._tcfg.update(cfg)
94 self._tcfg.update(cfg)
95 self._tcfg.update(self._ocfg)
95 self._tcfg.update(self._ocfg)
96 self._ucfg.update(cfg)
96 self._ucfg.update(cfg)
97 self._ucfg.update(self._ocfg)
97 self._ucfg.update(self._ocfg)
98
98
99 if root is None:
99 if root is None:
100 root = os.path.expanduser('~')
100 root = os.path.expanduser('~')
101 self.fixconfig(root=root)
101 self.fixconfig(root=root)
102
102
103 def fixconfig(self, root=None):
103 def fixconfig(self, root=None):
104 # translate paths relative to root (or home) into absolute paths
104 # translate paths relative to root (or home) into absolute paths
105 root = root or os.getcwd()
105 root = root or os.getcwd()
106 for c in self._tcfg, self._ucfg, self._ocfg:
106 for c in self._tcfg, self._ucfg, self._ocfg:
107 for n, p in c.items('paths'):
107 for n, p in c.items('paths'):
108 if p and "://" not in p and not os.path.isabs(p):
108 if p and "://" not in p and not os.path.isabs(p):
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
109 c.set("paths", n, os.path.normpath(os.path.join(root, p)))
110
110
111 # update ui options
111 # update ui options
112 self.debugflag = self.configbool('ui', 'debug')
112 self.debugflag = self.configbool('ui', 'debug')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
113 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
114 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
115 if self.verbose and self.quiet:
115 if self.verbose and self.quiet:
116 self.quiet = self.verbose = False
116 self.quiet = self.verbose = False
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
117 self._reportuntrusted = self.configbool("ui", "report_untrusted", True)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
118 self.tracebackflag = self.configbool('ui', 'traceback', False)
119
119
120 # update trust information
120 # update trust information
121 self._trustusers.update(self.configlist('trusted', 'users'))
121 self._trustusers.update(self.configlist('trusted', 'users'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
122 self._trustgroups.update(self.configlist('trusted', 'groups'))
123
123
124 def setconfig(self, section, name, value):
124 def setconfig(self, section, name, value):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
125 for cfg in (self._ocfg, self._tcfg, self._ucfg):
126 cfg.set(section, name, value)
126 cfg.set(section, name, value)
127 self.fixconfig()
127 self.fixconfig()
128
128
129 def _data(self, untrusted):
129 def _data(self, untrusted):
130 return untrusted and self._ucfg or self._tcfg
130 return untrusted and self._ucfg or self._tcfg
131
131
132 def configsource(self, section, name, untrusted=False):
132 def configsource(self, section, name, untrusted=False):
133 return self._data(untrusted).source(section, name) or 'none'
133 return self._data(untrusted).source(section, name) or 'none'
134
134
135 def config(self, section, name, default=None, untrusted=False):
135 def config(self, section, name, default=None, untrusted=False):
136 value = self._data(untrusted).get(section, name, default)
136 value = self._data(untrusted).get(section, name, default)
137 if self.debugflag and not untrusted and self._reportuntrusted:
137 if self.debugflag and not untrusted and self._reportuntrusted:
138 uvalue = self._ucfg.get(section, name)
138 uvalue = self._ucfg.get(section, name)
139 if uvalue is not None and uvalue != value:
139 if uvalue is not None and uvalue != value:
140 self.debug(_("ignoring untrusted configuration option "
140 self.debug(_("ignoring untrusted configuration option "
141 "%s.%s = %s\n") % (section, name, uvalue))
141 "%s.%s = %s\n") % (section, name, uvalue))
142 return value
142 return value
143
143
144 def configbool(self, section, name, default=False, untrusted=False):
144 def configbool(self, section, name, default=False, untrusted=False):
145 v = self.config(section, name, None, untrusted)
145 v = self.config(section, name, None, untrusted)
146 if v is None:
146 if v is None:
147 return default
147 return default
148 if isinstance(v, bool):
148 if isinstance(v, bool):
149 return v
149 return v
150 if v.lower() not in _booleans:
150 if v.lower() not in _booleans:
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
151 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
152 % (section, name, v))
152 % (section, name, v))
153 return _booleans[v.lower()]
153 return _booleans[v.lower()]
154
154
155 def configlist(self, section, name, default=None, untrusted=False):
155 def configlist(self, section, name, default=None, untrusted=False):
156 """Return a list of comma/space separated strings"""
156 """Return a list of comma/space separated strings"""
157
157
158 def _parse_plain(parts, s, offset):
158 def _parse_plain(parts, s, offset):
159 whitespace = False
159 whitespace = False
160 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
160 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
161 whitespace = True
161 whitespace = True
162 offset += 1
162 offset += 1
163 if offset >= len(s):
163 if offset >= len(s):
164 return None, parts, offset
164 return None, parts, offset
165 if whitespace:
165 if whitespace:
166 parts.append('')
166 parts.append('')
167 if s[offset] == '"' and not parts[-1]:
167 if s[offset] == '"' and not parts[-1]:
168 return _parse_quote, parts, offset + 1
168 return _parse_quote, parts, offset + 1
169 elif s[offset] == '"' and parts[-1][-1] == '\\':
169 elif s[offset] == '"' and parts[-1][-1] == '\\':
170 parts[-1] = parts[-1][:-1] + s[offset]
170 parts[-1] = parts[-1][:-1] + s[offset]
171 return _parse_plain, parts, offset + 1
171 return _parse_plain, parts, offset + 1
172 parts[-1] += s[offset]
172 parts[-1] += s[offset]
173 return _parse_plain, parts, offset + 1
173 return _parse_plain, parts, offset + 1
174
174
175 def _parse_quote(parts, s, offset):
175 def _parse_quote(parts, s, offset):
176 if offset < len(s) and s[offset] == '"': # ""
176 if offset < len(s) and s[offset] == '"': # ""
177 parts.append('')
177 parts.append('')
178 offset += 1
178 offset += 1
179 while offset < len(s) and (s[offset].isspace() or
179 while offset < len(s) and (s[offset].isspace() or
180 s[offset] == ','):
180 s[offset] == ','):
181 offset += 1
181 offset += 1
182 return _parse_plain, parts, offset
182 return _parse_plain, parts, offset
183
183
184 while offset < len(s) and s[offset] != '"':
184 while offset < len(s) and s[offset] != '"':
185 if s[offset] == '\\' and offset + 1 < len(s) and s[offset + 1] == '"':
185 if (s[offset] == '\\' and offset + 1 < len(s)
186 and s[offset + 1] == '"'):
186 offset += 1
187 offset += 1
187 parts[-1] += '"'
188 parts[-1] += '"'
188 else:
189 else:
189 parts[-1] += s[offset]
190 parts[-1] += s[offset]
190 offset += 1
191 offset += 1
191
192
192 if offset >= len(s):
193 if offset >= len(s):
193 real_parts = _configlist(parts[-1])
194 real_parts = _configlist(parts[-1])
194 if not real_parts:
195 if not real_parts:
195 parts[-1] = '"'
196 parts[-1] = '"'
196 else:
197 else:
197 real_parts[0] = '"' + real_parts[0]
198 real_parts[0] = '"' + real_parts[0]
198 parts = parts[:-1]
199 parts = parts[:-1]
199 parts.extend(real_parts)
200 parts.extend(real_parts)
200 return None, parts, offset
201 return None, parts, offset
201
202
202 offset += 1
203 offset += 1
203 while offset < len(s) and s[offset] in [' ', ',']:
204 while offset < len(s) and s[offset] in [' ', ',']:
204 offset += 1
205 offset += 1
205
206
206 if offset < len(s):
207 if offset < len(s):
207 if offset + 1 == len(s) and s[offset] == '"':
208 if offset + 1 == len(s) and s[offset] == '"':
208 parts[-1] += '"'
209 parts[-1] += '"'
209 offset += 1
210 offset += 1
210 else:
211 else:
211 parts.append('')
212 parts.append('')
212 else:
213 else:
213 return None, parts, offset
214 return None, parts, offset
214
215
215 return _parse_plain, parts, offset
216 return _parse_plain, parts, offset
216
217
217 def _configlist(s):
218 def _configlist(s):
218 s = s.rstrip(' ,')
219 s = s.rstrip(' ,')
219 if not s:
220 if not s:
220 return None
221 return None
221 parser, parts, offset = _parse_plain, [''], 0
222 parser, parts, offset = _parse_plain, [''], 0
222 while parser:
223 while parser:
223 parser, parts, offset = parser(parts, s, offset)
224 parser, parts, offset = parser(parts, s, offset)
224 return parts
225 return parts
225
226
226 result = self.config(section, name, untrusted=untrusted)
227 result = self.config(section, name, untrusted=untrusted)
227 if result is None:
228 if result is None:
228 result = default or []
229 result = default or []
229 if isinstance(result, basestring):
230 if isinstance(result, basestring):
230 result = _configlist(result)
231 result = _configlist(result)
231 if result is None:
232 if result is None:
232 result = default or []
233 result = default or []
233 return result
234 return result
234
235
235 def has_section(self, section, untrusted=False):
236 def has_section(self, section, untrusted=False):
236 '''tell whether section exists in config.'''
237 '''tell whether section exists in config.'''
237 return section in self._data(untrusted)
238 return section in self._data(untrusted)
238
239
239 def configitems(self, section, untrusted=False):
240 def configitems(self, section, untrusted=False):
240 items = self._data(untrusted).items(section)
241 items = self._data(untrusted).items(section)
241 if self.debugflag and not untrusted and self._reportuntrusted:
242 if self.debugflag and not untrusted and self._reportuntrusted:
242 for k, v in self._ucfg.items(section):
243 for k, v in self._ucfg.items(section):
243 if self._tcfg.get(section, k) != v:
244 if self._tcfg.get(section, k) != v:
244 self.debug(_("ignoring untrusted configuration option "
245 self.debug(_("ignoring untrusted configuration option "
245 "%s.%s = %s\n") % (section, k, v))
246 "%s.%s = %s\n") % (section, k, v))
246 return items
247 return items
247
248
248 def walkconfig(self, untrusted=False):
249 def walkconfig(self, untrusted=False):
249 cfg = self._data(untrusted)
250 cfg = self._data(untrusted)
250 for section in cfg.sections():
251 for section in cfg.sections():
251 for name, value in self.configitems(section, untrusted):
252 for name, value in self.configitems(section, untrusted):
252 yield section, name, str(value).replace('\n', '\\n')
253 yield section, name, str(value).replace('\n', '\\n')
253
254
254 def plain(self):
255 def plain(self):
255 return 'HGPLAIN' in os.environ
256 return 'HGPLAIN' in os.environ
256
257
257 def username(self):
258 def username(self):
258 """Return default username to be used in commits.
259 """Return default username to be used in commits.
259
260
260 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
261 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
261 and stop searching if one of these is set.
262 and stop searching if one of these is set.
262 If not found and ui.askusername is True, ask the user, else use
263 If not found and ui.askusername is True, ask the user, else use
263 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
264 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
264 """
265 """
265 user = os.environ.get("HGUSER")
266 user = os.environ.get("HGUSER")
266 if user is None:
267 if user is None:
267 user = self.config("ui", "username")
268 user = self.config("ui", "username")
268 if user is None:
269 if user is None:
269 user = os.environ.get("EMAIL")
270 user = os.environ.get("EMAIL")
270 if user is None and self.configbool("ui", "askusername"):
271 if user is None and self.configbool("ui", "askusername"):
271 user = self.prompt(_("enter a commit username:"), default=None)
272 user = self.prompt(_("enter a commit username:"), default=None)
272 if user is None and not self.interactive():
273 if user is None and not self.interactive():
273 try:
274 try:
274 user = '%s@%s' % (util.getuser(), socket.getfqdn())
275 user = '%s@%s' % (util.getuser(), socket.getfqdn())
275 self.warn(_("No username found, using '%s' instead\n") % user)
276 self.warn(_("No username found, using '%s' instead\n") % user)
276 except KeyError:
277 except KeyError:
277 pass
278 pass
278 if not user:
279 if not user:
279 raise util.Abort(_('no username supplied (see "hg help config")'))
280 raise util.Abort(_('no username supplied (see "hg help config")'))
280 if "\n" in user:
281 if "\n" in user:
281 raise util.Abort(_("username %s contains a newline\n") % repr(user))
282 raise util.Abort(_("username %s contains a newline\n") % repr(user))
282 return user
283 return user
283
284
284 def shortuser(self, user):
285 def shortuser(self, user):
285 """Return a short representation of a user name or email address."""
286 """Return a short representation of a user name or email address."""
286 if not self.verbose:
287 if not self.verbose:
287 user = util.shortuser(user)
288 user = util.shortuser(user)
288 return user
289 return user
289
290
290 def _path(self, loc):
291 def _path(self, loc):
291 p = self.config('paths', loc)
292 p = self.config('paths', loc)
292 if p:
293 if p:
293 if '%%' in p:
294 if '%%' in p:
294 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
295 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
295 (loc, p, self.configsource('paths', loc)))
296 (loc, p, self.configsource('paths', loc)))
296 p = p.replace('%%', '%')
297 p = p.replace('%%', '%')
297 p = util.expandpath(p)
298 p = util.expandpath(p)
298 return p
299 return p
299
300
300 def expandpath(self, loc, default=None):
301 def expandpath(self, loc, default=None):
301 """Return repository location relative to cwd or from [paths]"""
302 """Return repository location relative to cwd or from [paths]"""
302 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
303 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
303 return loc
304 return loc
304
305
305 path = self._path(loc)
306 path = self._path(loc)
306 if not path and default is not None:
307 if not path and default is not None:
307 path = self._path(default)
308 path = self._path(default)
308 return path or loc
309 return path or loc
309
310
310 def pushbuffer(self):
311 def pushbuffer(self):
311 self._buffers.append([])
312 self._buffers.append([])
312
313
313 def popbuffer(self, labeled=False):
314 def popbuffer(self, labeled=False):
314 '''pop the last buffer and return the buffered output
315 '''pop the last buffer and return the buffered output
315
316
316 If labeled is True, any labels associated with buffered
317 If labeled is True, any labels associated with buffered
317 output will be handled. By default, this has no effect
318 output will be handled. By default, this has no effect
318 on the output returned, but extensions and GUI tools may
319 on the output returned, but extensions and GUI tools may
319 handle this argument and returned styled output. If output
320 handle this argument and returned styled output. If output
320 is being buffered so it can be captured and parsed or
321 is being buffered so it can be captured and parsed or
321 processed, labeled should not be set to True.
322 processed, labeled should not be set to True.
322 '''
323 '''
323 return "".join(self._buffers.pop())
324 return "".join(self._buffers.pop())
324
325
325 def write(self, *args, **opts):
326 def write(self, *args, **opts):
326 '''write args to output
327 '''write args to output
327
328
328 By default, this method simply writes to the buffer or stdout,
329 By default, this method simply writes to the buffer or stdout,
329 but extensions or GUI tools may override this method,
330 but extensions or GUI tools may override this method,
330 write_err(), popbuffer(), and label() to style output from
331 write_err(), popbuffer(), and label() to style output from
331 various parts of hg.
332 various parts of hg.
332
333
333 An optional keyword argument, "label", can be passed in.
334 An optional keyword argument, "label", can be passed in.
334 This should be a string containing label names separated by
335 This should be a string containing label names separated by
335 space. Label names take the form of "topic.type". For example,
336 space. Label names take the form of "topic.type". For example,
336 ui.debug() issues a label of "ui.debug".
337 ui.debug() issues a label of "ui.debug".
337
338
338 When labeling output for a specific command, a label of
339 When labeling output for a specific command, a label of
339 "cmdname.type" is recommended. For example, status issues
340 "cmdname.type" is recommended. For example, status issues
340 a label of "status.modified" for modified files.
341 a label of "status.modified" for modified files.
341 '''
342 '''
342 if self._buffers:
343 if self._buffers:
343 self._buffers[-1].extend([str(a) for a in args])
344 self._buffers[-1].extend([str(a) for a in args])
344 else:
345 else:
345 for a in args:
346 for a in args:
346 sys.stdout.write(str(a))
347 sys.stdout.write(str(a))
347
348
348 def write_err(self, *args, **opts):
349 def write_err(self, *args, **opts):
349 try:
350 try:
350 if not getattr(sys.stdout, 'closed', False):
351 if not getattr(sys.stdout, 'closed', False):
351 sys.stdout.flush()
352 sys.stdout.flush()
352 for a in args:
353 for a in args:
353 sys.stderr.write(str(a))
354 sys.stderr.write(str(a))
354 # stderr may be buffered under win32 when redirected to files,
355 # stderr may be buffered under win32 when redirected to files,
355 # including stdout.
356 # including stdout.
356 if not getattr(sys.stderr, 'closed', False):
357 if not getattr(sys.stderr, 'closed', False):
357 sys.stderr.flush()
358 sys.stderr.flush()
358 except IOError, inst:
359 except IOError, inst:
359 if inst.errno != errno.EPIPE:
360 if inst.errno != errno.EPIPE:
360 raise
361 raise
361
362
362 def flush(self):
363 def flush(self):
363 try: sys.stdout.flush()
364 try: sys.stdout.flush()
364 except: pass
365 except: pass
365 try: sys.stderr.flush()
366 try: sys.stderr.flush()
366 except: pass
367 except: pass
367
368
368 def interactive(self):
369 def interactive(self):
369 i = self.configbool("ui", "interactive", None)
370 i = self.configbool("ui", "interactive", None)
370 if i is None:
371 if i is None:
371 try:
372 try:
372 return sys.stdin.isatty()
373 return sys.stdin.isatty()
373 except AttributeError:
374 except AttributeError:
374 # some environments replace stdin without implementing isatty
375 # some environments replace stdin without implementing isatty
375 # usually those are non-interactive
376 # usually those are non-interactive
376 return False
377 return False
377
378
378 return i
379 return i
379
380
380 def _readline(self, prompt=''):
381 def _readline(self, prompt=''):
381 if sys.stdin.isatty():
382 if sys.stdin.isatty():
382 try:
383 try:
383 # magically add command line editing support, where
384 # magically add command line editing support, where
384 # available
385 # available
385 import readline
386 import readline
386 # force demandimport to really load the module
387 # force demandimport to really load the module
387 readline.read_history_file
388 readline.read_history_file
388 # windows sometimes raises something other than ImportError
389 # windows sometimes raises something other than ImportError
389 except Exception:
390 except Exception:
390 pass
391 pass
391 line = raw_input(prompt)
392 line = raw_input(prompt)
392 # When stdin is in binary mode on Windows, it can cause
393 # When stdin is in binary mode on Windows, it can cause
393 # raw_input() to emit an extra trailing carriage return
394 # raw_input() to emit an extra trailing carriage return
394 if os.linesep == '\r\n' and line and line[-1] == '\r':
395 if os.linesep == '\r\n' and line and line[-1] == '\r':
395 line = line[:-1]
396 line = line[:-1]
396 return line
397 return line
397
398
398 def prompt(self, msg, default="y"):
399 def prompt(self, msg, default="y"):
399 """Prompt user with msg, read response.
400 """Prompt user with msg, read response.
400 If ui is not interactive, the default is returned.
401 If ui is not interactive, the default is returned.
401 """
402 """
402 if not self.interactive():
403 if not self.interactive():
403 self.write(msg, ' ', default, "\n")
404 self.write(msg, ' ', default, "\n")
404 return default
405 return default
405 try:
406 try:
406 r = self._readline(msg + ' ')
407 r = self._readline(msg + ' ')
407 if not r:
408 if not r:
408 return default
409 return default
409 return r
410 return r
410 except EOFError:
411 except EOFError:
411 raise util.Abort(_('response expected'))
412 raise util.Abort(_('response expected'))
412
413
413 def promptchoice(self, msg, choices, default=0):
414 def promptchoice(self, msg, choices, default=0):
414 """Prompt user with msg, read response, and ensure it matches
415 """Prompt user with msg, read response, and ensure it matches
415 one of the provided choices. The index of the choice is returned.
416 one of the provided choices. The index of the choice is returned.
416 choices is a sequence of acceptable responses with the format:
417 choices is a sequence of acceptable responses with the format:
417 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
418 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
418 If ui is not interactive, the default is returned.
419 If ui is not interactive, the default is returned.
419 """
420 """
420 resps = [s[s.index('&')+1].lower() for s in choices]
421 resps = [s[s.index('&')+1].lower() for s in choices]
421 while True:
422 while True:
422 r = self.prompt(msg, resps[default])
423 r = self.prompt(msg, resps[default])
423 if r.lower() in resps:
424 if r.lower() in resps:
424 return resps.index(r.lower())
425 return resps.index(r.lower())
425 self.write(_("unrecognized response\n"))
426 self.write(_("unrecognized response\n"))
426
427
427 def getpass(self, prompt=None, default=None):
428 def getpass(self, prompt=None, default=None):
428 if not self.interactive():
429 if not self.interactive():
429 return default
430 return default
430 try:
431 try:
431 return getpass.getpass(prompt or _('password: '))
432 return getpass.getpass(prompt or _('password: '))
432 except EOFError:
433 except EOFError:
433 raise util.Abort(_('response expected'))
434 raise util.Abort(_('response expected'))
434 def status(self, *msg, **opts):
435 def status(self, *msg, **opts):
435 '''write status message to output (if ui.quiet is False)
436 '''write status message to output (if ui.quiet is False)
436
437
437 This adds an output label of "ui.status".
438 This adds an output label of "ui.status".
438 '''
439 '''
439 if not self.quiet:
440 if not self.quiet:
440 opts['label'] = opts.get('label', '') + ' ui.status'
441 opts['label'] = opts.get('label', '') + ' ui.status'
441 self.write(*msg, **opts)
442 self.write(*msg, **opts)
442 def warn(self, *msg, **opts):
443 def warn(self, *msg, **opts):
443 '''write warning message to output (stderr)
444 '''write warning message to output (stderr)
444
445
445 This adds an output label of "ui.warning".
446 This adds an output label of "ui.warning".
446 '''
447 '''
447 opts['label'] = opts.get('label', '') + ' ui.warning'
448 opts['label'] = opts.get('label', '') + ' ui.warning'
448 self.write_err(*msg, **opts)
449 self.write_err(*msg, **opts)
449 def note(self, *msg, **opts):
450 def note(self, *msg, **opts):
450 '''write note to output (if ui.verbose is True)
451 '''write note to output (if ui.verbose is True)
451
452
452 This adds an output label of "ui.note".
453 This adds an output label of "ui.note".
453 '''
454 '''
454 if self.verbose:
455 if self.verbose:
455 opts['label'] = opts.get('label', '') + ' ui.note'
456 opts['label'] = opts.get('label', '') + ' ui.note'
456 self.write(*msg, **opts)
457 self.write(*msg, **opts)
457 def debug(self, *msg, **opts):
458 def debug(self, *msg, **opts):
458 '''write debug message to output (if ui.debugflag is True)
459 '''write debug message to output (if ui.debugflag is True)
459
460
460 This adds an output label of "ui.debug".
461 This adds an output label of "ui.debug".
461 '''
462 '''
462 if self.debugflag:
463 if self.debugflag:
463 opts['label'] = opts.get('label', '') + ' ui.debug'
464 opts['label'] = opts.get('label', '') + ' ui.debug'
464 self.write(*msg, **opts)
465 self.write(*msg, **opts)
465 def edit(self, text, user):
466 def edit(self, text, user):
466 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
467 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
467 text=True)
468 text=True)
468 try:
469 try:
469 f = os.fdopen(fd, "w")
470 f = os.fdopen(fd, "w")
470 f.write(text)
471 f.write(text)
471 f.close()
472 f.close()
472
473
473 editor = self.geteditor()
474 editor = self.geteditor()
474
475
475 util.system("%s \"%s\"" % (editor, name),
476 util.system("%s \"%s\"" % (editor, name),
476 environ={'HGUSER': user},
477 environ={'HGUSER': user},
477 onerr=util.Abort, errprefix=_("edit failed"))
478 onerr=util.Abort, errprefix=_("edit failed"))
478
479
479 f = open(name)
480 f = open(name)
480 t = f.read()
481 t = f.read()
481 f.close()
482 f.close()
482 finally:
483 finally:
483 os.unlink(name)
484 os.unlink(name)
484
485
485 return t
486 return t
486
487
487 def traceback(self, exc=None):
488 def traceback(self, exc=None):
488 '''print exception traceback if traceback printing enabled.
489 '''print exception traceback if traceback printing enabled.
489 only to call in exception handler. returns true if traceback
490 only to call in exception handler. returns true if traceback
490 printed.'''
491 printed.'''
491 if self.tracebackflag:
492 if self.tracebackflag:
492 if exc:
493 if exc:
493 traceback.print_exception(exc[0], exc[1], exc[2])
494 traceback.print_exception(exc[0], exc[1], exc[2])
494 else:
495 else:
495 traceback.print_exc()
496 traceback.print_exc()
496 return self.tracebackflag
497 return self.tracebackflag
497
498
498 def geteditor(self):
499 def geteditor(self):
499 '''return editor to use'''
500 '''return editor to use'''
500 return (os.environ.get("HGEDITOR") or
501 return (os.environ.get("HGEDITOR") or
501 self.config("ui", "editor") or
502 self.config("ui", "editor") or
502 os.environ.get("VISUAL") or
503 os.environ.get("VISUAL") or
503 os.environ.get("EDITOR", "vi"))
504 os.environ.get("EDITOR", "vi"))
504
505
505 def progress(self, topic, pos, item="", unit="", total=None):
506 def progress(self, topic, pos, item="", unit="", total=None):
506 '''show a progress message
507 '''show a progress message
507
508
508 With stock hg, this is simply a debug message that is hidden
509 With stock hg, this is simply a debug message that is hidden
509 by default, but with extensions or GUI tools it may be
510 by default, but with extensions or GUI tools it may be
510 visible. 'topic' is the current operation, 'item' is a
511 visible. 'topic' is the current operation, 'item' is a
511 non-numeric marker of the current position (ie the currently
512 non-numeric marker of the current position (ie the currently
512 in-process file), 'pos' is the current numeric position (ie
513 in-process file), 'pos' is the current numeric position (ie
513 revision, bytes, etc.), unit is a corresponding unit label,
514 revision, bytes, etc.), unit is a corresponding unit label,
514 and total is the highest expected pos.
515 and total is the highest expected pos.
515
516
516 Multiple nested topics may be active at a time.
517 Multiple nested topics may be active at a time.
517
518
518 All topics should be marked closed by setting pos to None at
519 All topics should be marked closed by setting pos to None at
519 termination.
520 termination.
520 '''
521 '''
521
522
522 if pos == None or not self.debugflag:
523 if pos == None or not self.debugflag:
523 return
524 return
524
525
525 if unit:
526 if unit:
526 unit = ' ' + unit
527 unit = ' ' + unit
527 if item:
528 if item:
528 item = ' ' + item
529 item = ' ' + item
529
530
530 if total:
531 if total:
531 pct = 100.0 * pos / total
532 pct = 100.0 * pos / total
532 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
533 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
533 % (topic, item, pos, total, unit, pct))
534 % (topic, item, pos, total, unit, pct))
534 else:
535 else:
535 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
536 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
536
537
537 def label(self, msg, label):
538 def label(self, msg, label):
538 '''style msg based on supplied label
539 '''style msg based on supplied label
539
540
540 Like ui.write(), this just returns msg unchanged, but extensions
541 Like ui.write(), this just returns msg unchanged, but extensions
541 and GUI tools can override it to allow styling output without
542 and GUI tools can override it to allow styling output without
542 writing it.
543 writing it.
543
544
544 ui.write(s, 'label') is equivalent to
545 ui.write(s, 'label') is equivalent to
545 ui.write(ui.label(s, 'label')).
546 ui.write(ui.label(s, 'label')).
546 '''
547 '''
547 return msg
548 return msg
General Comments 0
You need to be logged in to leave comments. Login now