##// END OF EJS Templates
ui: document the formatted(), interactive() & plain() functions.
Dan Villiom Podlaski Christiansen -
r11325:22a73730 default
parent child Browse files
Show More
@@ -1,565 +1,604 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)
185 if (s[offset] == '\\' and offset + 1 < len(s)
186 and s[offset + 1] == '"'):
186 and s[offset + 1] == '"'):
187 offset += 1
187 offset += 1
188 parts[-1] += '"'
188 parts[-1] += '"'
189 else:
189 else:
190 parts[-1] += s[offset]
190 parts[-1] += s[offset]
191 offset += 1
191 offset += 1
192
192
193 if offset >= len(s):
193 if offset >= len(s):
194 real_parts = _configlist(parts[-1])
194 real_parts = _configlist(parts[-1])
195 if not real_parts:
195 if not real_parts:
196 parts[-1] = '"'
196 parts[-1] = '"'
197 else:
197 else:
198 real_parts[0] = '"' + real_parts[0]
198 real_parts[0] = '"' + real_parts[0]
199 parts = parts[:-1]
199 parts = parts[:-1]
200 parts.extend(real_parts)
200 parts.extend(real_parts)
201 return None, parts, offset
201 return None, parts, offset
202
202
203 offset += 1
203 offset += 1
204 while offset < len(s) and s[offset] in [' ', ',']:
204 while offset < len(s) and s[offset] in [' ', ',']:
205 offset += 1
205 offset += 1
206
206
207 if offset < len(s):
207 if offset < len(s):
208 if offset + 1 == len(s) and s[offset] == '"':
208 if offset + 1 == len(s) and s[offset] == '"':
209 parts[-1] += '"'
209 parts[-1] += '"'
210 offset += 1
210 offset += 1
211 else:
211 else:
212 parts.append('')
212 parts.append('')
213 else:
213 else:
214 return None, parts, offset
214 return None, parts, offset
215
215
216 return _parse_plain, parts, offset
216 return _parse_plain, parts, offset
217
217
218 def _configlist(s):
218 def _configlist(s):
219 s = s.rstrip(' ,')
219 s = s.rstrip(' ,')
220 if not s:
220 if not s:
221 return None
221 return None
222 parser, parts, offset = _parse_plain, [''], 0
222 parser, parts, offset = _parse_plain, [''], 0
223 while parser:
223 while parser:
224 parser, parts, offset = parser(parts, s, offset)
224 parser, parts, offset = parser(parts, s, offset)
225 return parts
225 return parts
226
226
227 result = self.config(section, name, untrusted=untrusted)
227 result = self.config(section, name, untrusted=untrusted)
228 if result is None:
228 if result is None:
229 result = default or []
229 result = default or []
230 if isinstance(result, basestring):
230 if isinstance(result, basestring):
231 result = _configlist(result.lstrip(' ,\n'))
231 result = _configlist(result.lstrip(' ,\n'))
232 if result is None:
232 if result is None:
233 result = default or []
233 result = default or []
234 return result
234 return result
235
235
236 def has_section(self, section, untrusted=False):
236 def has_section(self, section, untrusted=False):
237 '''tell whether section exists in config.'''
237 '''tell whether section exists in config.'''
238 return section in self._data(untrusted)
238 return section in self._data(untrusted)
239
239
240 def configitems(self, section, untrusted=False):
240 def configitems(self, section, untrusted=False):
241 items = self._data(untrusted).items(section)
241 items = self._data(untrusted).items(section)
242 if self.debugflag and not untrusted and self._reportuntrusted:
242 if self.debugflag and not untrusted and self._reportuntrusted:
243 for k, v in self._ucfg.items(section):
243 for k, v in self._ucfg.items(section):
244 if self._tcfg.get(section, k) != v:
244 if self._tcfg.get(section, k) != v:
245 self.debug(_("ignoring untrusted configuration option "
245 self.debug(_("ignoring untrusted configuration option "
246 "%s.%s = %s\n") % (section, k, v))
246 "%s.%s = %s\n") % (section, k, v))
247 return items
247 return items
248
248
249 def walkconfig(self, untrusted=False):
249 def walkconfig(self, untrusted=False):
250 cfg = self._data(untrusted)
250 cfg = self._data(untrusted)
251 for section in cfg.sections():
251 for section in cfg.sections():
252 for name, value in self.configitems(section, untrusted):
252 for name, value in self.configitems(section, untrusted):
253 yield section, name, str(value).replace('\n', '\\n')
253 yield section, name, str(value).replace('\n', '\\n')
254
254
255 def plain(self):
255 def plain(self):
256 '''is plain mode active?
257
258 Plain mode means that all configuration variables which affect the
259 behavior and output of Mercurial should be ignored. Additionally, the
260 output should be stable, reproducible and suitable for use in scripts or
261 applications.
262
263 The only way to trigger plain mode is by setting the `HGPLAIN'
264 environment variable.
265 '''
256 return 'HGPLAIN' in os.environ
266 return 'HGPLAIN' in os.environ
257
267
258 def username(self):
268 def username(self):
259 """Return default username to be used in commits.
269 """Return default username to be used in commits.
260
270
261 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
271 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
262 and stop searching if one of these is set.
272 and stop searching if one of these is set.
263 If not found and ui.askusername is True, ask the user, else use
273 If not found and ui.askusername is True, ask the user, else use
264 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
274 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
265 """
275 """
266 user = os.environ.get("HGUSER")
276 user = os.environ.get("HGUSER")
267 if user is None:
277 if user is None:
268 user = self.config("ui", "username")
278 user = self.config("ui", "username")
269 if user is not None:
279 if user is not None:
270 user = os.path.expandvars(user)
280 user = os.path.expandvars(user)
271 if user is None:
281 if user is None:
272 user = os.environ.get("EMAIL")
282 user = os.environ.get("EMAIL")
273 if user is None and self.configbool("ui", "askusername"):
283 if user is None and self.configbool("ui", "askusername"):
274 user = self.prompt(_("enter a commit username:"), default=None)
284 user = self.prompt(_("enter a commit username:"), default=None)
275 if user is None and not self.interactive():
285 if user is None and not self.interactive():
276 try:
286 try:
277 user = '%s@%s' % (util.getuser(), socket.getfqdn())
287 user = '%s@%s' % (util.getuser(), socket.getfqdn())
278 self.warn(_("No username found, using '%s' instead\n") % user)
288 self.warn(_("No username found, using '%s' instead\n") % user)
279 except KeyError:
289 except KeyError:
280 pass
290 pass
281 if not user:
291 if not user:
282 raise util.Abort(_('no username supplied (see "hg help config")'))
292 raise util.Abort(_('no username supplied (see "hg help config")'))
283 if "\n" in user:
293 if "\n" in user:
284 raise util.Abort(_("username %s contains a newline\n") % repr(user))
294 raise util.Abort(_("username %s contains a newline\n") % repr(user))
285 return user
295 return user
286
296
287 def shortuser(self, user):
297 def shortuser(self, user):
288 """Return a short representation of a user name or email address."""
298 """Return a short representation of a user name or email address."""
289 if not self.verbose:
299 if not self.verbose:
290 user = util.shortuser(user)
300 user = util.shortuser(user)
291 return user
301 return user
292
302
293 def _path(self, loc):
303 def _path(self, loc):
294 p = self.config('paths', loc)
304 p = self.config('paths', loc)
295 if p:
305 if p:
296 if '%%' in p:
306 if '%%' in p:
297 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
307 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
298 (loc, p, self.configsource('paths', loc)))
308 (loc, p, self.configsource('paths', loc)))
299 p = p.replace('%%', '%')
309 p = p.replace('%%', '%')
300 p = util.expandpath(p)
310 p = util.expandpath(p)
301 return p
311 return p
302
312
303 def expandpath(self, loc, default=None):
313 def expandpath(self, loc, default=None):
304 """Return repository location relative to cwd or from [paths]"""
314 """Return repository location relative to cwd or from [paths]"""
305 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
315 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
306 return loc
316 return loc
307
317
308 path = self._path(loc)
318 path = self._path(loc)
309 if not path and default is not None:
319 if not path and default is not None:
310 path = self._path(default)
320 path = self._path(default)
311 return path or loc
321 return path or loc
312
322
313 def pushbuffer(self):
323 def pushbuffer(self):
314 self._buffers.append([])
324 self._buffers.append([])
315
325
316 def popbuffer(self, labeled=False):
326 def popbuffer(self, labeled=False):
317 '''pop the last buffer and return the buffered output
327 '''pop the last buffer and return the buffered output
318
328
319 If labeled is True, any labels associated with buffered
329 If labeled is True, any labels associated with buffered
320 output will be handled. By default, this has no effect
330 output will be handled. By default, this has no effect
321 on the output returned, but extensions and GUI tools may
331 on the output returned, but extensions and GUI tools may
322 handle this argument and returned styled output. If output
332 handle this argument and returned styled output. If output
323 is being buffered so it can be captured and parsed or
333 is being buffered so it can be captured and parsed or
324 processed, labeled should not be set to True.
334 processed, labeled should not be set to True.
325 '''
335 '''
326 return "".join(self._buffers.pop())
336 return "".join(self._buffers.pop())
327
337
328 def write(self, *args, **opts):
338 def write(self, *args, **opts):
329 '''write args to output
339 '''write args to output
330
340
331 By default, this method simply writes to the buffer or stdout,
341 By default, this method simply writes to the buffer or stdout,
332 but extensions or GUI tools may override this method,
342 but extensions or GUI tools may override this method,
333 write_err(), popbuffer(), and label() to style output from
343 write_err(), popbuffer(), and label() to style output from
334 various parts of hg.
344 various parts of hg.
335
345
336 An optional keyword argument, "label", can be passed in.
346 An optional keyword argument, "label", can be passed in.
337 This should be a string containing label names separated by
347 This should be a string containing label names separated by
338 space. Label names take the form of "topic.type". For example,
348 space. Label names take the form of "topic.type". For example,
339 ui.debug() issues a label of "ui.debug".
349 ui.debug() issues a label of "ui.debug".
340
350
341 When labeling output for a specific command, a label of
351 When labeling output for a specific command, a label of
342 "cmdname.type" is recommended. For example, status issues
352 "cmdname.type" is recommended. For example, status issues
343 a label of "status.modified" for modified files.
353 a label of "status.modified" for modified files.
344 '''
354 '''
345 if self._buffers:
355 if self._buffers:
346 self._buffers[-1].extend([str(a) for a in args])
356 self._buffers[-1].extend([str(a) for a in args])
347 else:
357 else:
348 for a in args:
358 for a in args:
349 sys.stdout.write(str(a))
359 sys.stdout.write(str(a))
350
360
351 def write_err(self, *args, **opts):
361 def write_err(self, *args, **opts):
352 try:
362 try:
353 if not getattr(sys.stdout, 'closed', False):
363 if not getattr(sys.stdout, 'closed', False):
354 sys.stdout.flush()
364 sys.stdout.flush()
355 for a in args:
365 for a in args:
356 sys.stderr.write(str(a))
366 sys.stderr.write(str(a))
357 # stderr may be buffered under win32 when redirected to files,
367 # stderr may be buffered under win32 when redirected to files,
358 # including stdout.
368 # including stdout.
359 if not getattr(sys.stderr, 'closed', False):
369 if not getattr(sys.stderr, 'closed', False):
360 sys.stderr.flush()
370 sys.stderr.flush()
361 except IOError, inst:
371 except IOError, inst:
362 if inst.errno != errno.EPIPE:
372 if inst.errno != errno.EPIPE:
363 raise
373 raise
364
374
365 def flush(self):
375 def flush(self):
366 try: sys.stdout.flush()
376 try: sys.stdout.flush()
367 except: pass
377 except: pass
368 try: sys.stderr.flush()
378 try: sys.stderr.flush()
369 except: pass
379 except: pass
370
380
371 def interactive(self):
381 def interactive(self):
382 '''is interactive input allowed?
383
384 An interactive session is a session where input can be reasonably read
385 from `sys.stdin'. If this function returns false, any attempt to read
386 from stdin should fail with an error, unless a sensible default has been
387 specified.
388
389 Interactiveness is triggered by the value of the `ui.interactive'
390 configuration variable or - if it is unset - when `sys.stdin' points
391 to a terminal device.
392
393 This function refers to input only; for output, see `ui.formatted()'.
394 '''
372 i = self.configbool("ui", "interactive", None)
395 i = self.configbool("ui", "interactive", None)
373 if i is None:
396 if i is None:
374 try:
397 try:
375 return sys.stdin.isatty()
398 return sys.stdin.isatty()
376 except AttributeError:
399 except AttributeError:
377 # some environments replace stdin without implementing isatty
400 # some environments replace stdin without implementing isatty
378 # usually those are non-interactive
401 # usually those are non-interactive
379 return False
402 return False
380
403
381 return i
404 return i
382
405
383 def formatted(self):
406 def formatted(self):
407 '''should formatted output be used?
408
409 It is often desirable to format the output to suite the output medium.
410 Examples of this are truncating long lines or colorizing messages.
411 However, this is not often not desirable when piping output into other
412 utilities, e.g. `grep'.
413
414 Formatted output is triggered by the value of the `ui.formatted'
415 configuration variable or - if it is unset - when `sys.stdout' points
416 to a terminal device. Please note that `ui.formatted' should be
417 considered an implementation detail; it is not intended for use outside
418 Mercurial or its extensions.
419
420 This function refers to output only; for input, see `ui.interactive()'.
421 This function always returns false when in plain mode, see `ui.plain()'.
422 '''
384 if self.plain():
423 if self.plain():
385 return False
424 return False
386
425
387 i = self.configbool("ui", "formatted", None)
426 i = self.configbool("ui", "formatted", None)
388 if i is None:
427 if i is None:
389 try:
428 try:
390 return sys.stdout.isatty()
429 return sys.stdout.isatty()
391 except AttributeError:
430 except AttributeError:
392 # some environments replace stdout without implementing isatty
431 # some environments replace stdout without implementing isatty
393 # usually those are non-interactive
432 # usually those are non-interactive
394 return False
433 return False
395
434
396 return i
435 return i
397
436
398 def _readline(self, prompt=''):
437 def _readline(self, prompt=''):
399 if sys.stdin.isatty():
438 if sys.stdin.isatty():
400 try:
439 try:
401 # magically add command line editing support, where
440 # magically add command line editing support, where
402 # available
441 # available
403 import readline
442 import readline
404 # force demandimport to really load the module
443 # force demandimport to really load the module
405 readline.read_history_file
444 readline.read_history_file
406 # windows sometimes raises something other than ImportError
445 # windows sometimes raises something other than ImportError
407 except Exception:
446 except Exception:
408 pass
447 pass
409 line = raw_input(prompt)
448 line = raw_input(prompt)
410 # When stdin is in binary mode on Windows, it can cause
449 # When stdin is in binary mode on Windows, it can cause
411 # raw_input() to emit an extra trailing carriage return
450 # raw_input() to emit an extra trailing carriage return
412 if os.linesep == '\r\n' and line and line[-1] == '\r':
451 if os.linesep == '\r\n' and line and line[-1] == '\r':
413 line = line[:-1]
452 line = line[:-1]
414 return line
453 return line
415
454
416 def prompt(self, msg, default="y"):
455 def prompt(self, msg, default="y"):
417 """Prompt user with msg, read response.
456 """Prompt user with msg, read response.
418 If ui is not interactive, the default is returned.
457 If ui is not interactive, the default is returned.
419 """
458 """
420 if not self.interactive():
459 if not self.interactive():
421 self.write(msg, ' ', default, "\n")
460 self.write(msg, ' ', default, "\n")
422 return default
461 return default
423 try:
462 try:
424 r = self._readline(msg + ' ')
463 r = self._readline(msg + ' ')
425 if not r:
464 if not r:
426 return default
465 return default
427 return r
466 return r
428 except EOFError:
467 except EOFError:
429 raise util.Abort(_('response expected'))
468 raise util.Abort(_('response expected'))
430
469
431 def promptchoice(self, msg, choices, default=0):
470 def promptchoice(self, msg, choices, default=0):
432 """Prompt user with msg, read response, and ensure it matches
471 """Prompt user with msg, read response, and ensure it matches
433 one of the provided choices. The index of the choice is returned.
472 one of the provided choices. The index of the choice is returned.
434 choices is a sequence of acceptable responses with the format:
473 choices is a sequence of acceptable responses with the format:
435 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
474 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
436 If ui is not interactive, the default is returned.
475 If ui is not interactive, the default is returned.
437 """
476 """
438 resps = [s[s.index('&')+1].lower() for s in choices]
477 resps = [s[s.index('&')+1].lower() for s in choices]
439 while True:
478 while True:
440 r = self.prompt(msg, resps[default])
479 r = self.prompt(msg, resps[default])
441 if r.lower() in resps:
480 if r.lower() in resps:
442 return resps.index(r.lower())
481 return resps.index(r.lower())
443 self.write(_("unrecognized response\n"))
482 self.write(_("unrecognized response\n"))
444
483
445 def getpass(self, prompt=None, default=None):
484 def getpass(self, prompt=None, default=None):
446 if not self.interactive():
485 if not self.interactive():
447 return default
486 return default
448 try:
487 try:
449 return getpass.getpass(prompt or _('password: '))
488 return getpass.getpass(prompt or _('password: '))
450 except EOFError:
489 except EOFError:
451 raise util.Abort(_('response expected'))
490 raise util.Abort(_('response expected'))
452 def status(self, *msg, **opts):
491 def status(self, *msg, **opts):
453 '''write status message to output (if ui.quiet is False)
492 '''write status message to output (if ui.quiet is False)
454
493
455 This adds an output label of "ui.status".
494 This adds an output label of "ui.status".
456 '''
495 '''
457 if not self.quiet:
496 if not self.quiet:
458 opts['label'] = opts.get('label', '') + ' ui.status'
497 opts['label'] = opts.get('label', '') + ' ui.status'
459 self.write(*msg, **opts)
498 self.write(*msg, **opts)
460 def warn(self, *msg, **opts):
499 def warn(self, *msg, **opts):
461 '''write warning message to output (stderr)
500 '''write warning message to output (stderr)
462
501
463 This adds an output label of "ui.warning".
502 This adds an output label of "ui.warning".
464 '''
503 '''
465 opts['label'] = opts.get('label', '') + ' ui.warning'
504 opts['label'] = opts.get('label', '') + ' ui.warning'
466 self.write_err(*msg, **opts)
505 self.write_err(*msg, **opts)
467 def note(self, *msg, **opts):
506 def note(self, *msg, **opts):
468 '''write note to output (if ui.verbose is True)
507 '''write note to output (if ui.verbose is True)
469
508
470 This adds an output label of "ui.note".
509 This adds an output label of "ui.note".
471 '''
510 '''
472 if self.verbose:
511 if self.verbose:
473 opts['label'] = opts.get('label', '') + ' ui.note'
512 opts['label'] = opts.get('label', '') + ' ui.note'
474 self.write(*msg, **opts)
513 self.write(*msg, **opts)
475 def debug(self, *msg, **opts):
514 def debug(self, *msg, **opts):
476 '''write debug message to output (if ui.debugflag is True)
515 '''write debug message to output (if ui.debugflag is True)
477
516
478 This adds an output label of "ui.debug".
517 This adds an output label of "ui.debug".
479 '''
518 '''
480 if self.debugflag:
519 if self.debugflag:
481 opts['label'] = opts.get('label', '') + ' ui.debug'
520 opts['label'] = opts.get('label', '') + ' ui.debug'
482 self.write(*msg, **opts)
521 self.write(*msg, **opts)
483 def edit(self, text, user):
522 def edit(self, text, user):
484 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
523 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
485 text=True)
524 text=True)
486 try:
525 try:
487 f = os.fdopen(fd, "w")
526 f = os.fdopen(fd, "w")
488 f.write(text)
527 f.write(text)
489 f.close()
528 f.close()
490
529
491 editor = self.geteditor()
530 editor = self.geteditor()
492
531
493 util.system("%s \"%s\"" % (editor, name),
532 util.system("%s \"%s\"" % (editor, name),
494 environ={'HGUSER': user},
533 environ={'HGUSER': user},
495 onerr=util.Abort, errprefix=_("edit failed"))
534 onerr=util.Abort, errprefix=_("edit failed"))
496
535
497 f = open(name)
536 f = open(name)
498 t = f.read()
537 t = f.read()
499 f.close()
538 f.close()
500 finally:
539 finally:
501 os.unlink(name)
540 os.unlink(name)
502
541
503 return t
542 return t
504
543
505 def traceback(self, exc=None):
544 def traceback(self, exc=None):
506 '''print exception traceback if traceback printing enabled.
545 '''print exception traceback if traceback printing enabled.
507 only to call in exception handler. returns true if traceback
546 only to call in exception handler. returns true if traceback
508 printed.'''
547 printed.'''
509 if self.tracebackflag:
548 if self.tracebackflag:
510 if exc:
549 if exc:
511 traceback.print_exception(exc[0], exc[1], exc[2])
550 traceback.print_exception(exc[0], exc[1], exc[2])
512 else:
551 else:
513 traceback.print_exc()
552 traceback.print_exc()
514 return self.tracebackflag
553 return self.tracebackflag
515
554
516 def geteditor(self):
555 def geteditor(self):
517 '''return editor to use'''
556 '''return editor to use'''
518 return (os.environ.get("HGEDITOR") or
557 return (os.environ.get("HGEDITOR") or
519 self.config("ui", "editor") or
558 self.config("ui", "editor") or
520 os.environ.get("VISUAL") or
559 os.environ.get("VISUAL") or
521 os.environ.get("EDITOR", "vi"))
560 os.environ.get("EDITOR", "vi"))
522
561
523 def progress(self, topic, pos, item="", unit="", total=None):
562 def progress(self, topic, pos, item="", unit="", total=None):
524 '''show a progress message
563 '''show a progress message
525
564
526 With stock hg, this is simply a debug message that is hidden
565 With stock hg, this is simply a debug message that is hidden
527 by default, but with extensions or GUI tools it may be
566 by default, but with extensions or GUI tools it may be
528 visible. 'topic' is the current operation, 'item' is a
567 visible. 'topic' is the current operation, 'item' is a
529 non-numeric marker of the current position (ie the currently
568 non-numeric marker of the current position (ie the currently
530 in-process file), 'pos' is the current numeric position (ie
569 in-process file), 'pos' is the current numeric position (ie
531 revision, bytes, etc.), unit is a corresponding unit label,
570 revision, bytes, etc.), unit is a corresponding unit label,
532 and total is the highest expected pos.
571 and total is the highest expected pos.
533
572
534 Multiple nested topics may be active at a time.
573 Multiple nested topics may be active at a time.
535
574
536 All topics should be marked closed by setting pos to None at
575 All topics should be marked closed by setting pos to None at
537 termination.
576 termination.
538 '''
577 '''
539
578
540 if pos == None or not self.debugflag:
579 if pos == None or not self.debugflag:
541 return
580 return
542
581
543 if unit:
582 if unit:
544 unit = ' ' + unit
583 unit = ' ' + unit
545 if item:
584 if item:
546 item = ' ' + item
585 item = ' ' + item
547
586
548 if total:
587 if total:
549 pct = 100.0 * pos / total
588 pct = 100.0 * pos / total
550 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
589 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
551 % (topic, item, pos, total, unit, pct))
590 % (topic, item, pos, total, unit, pct))
552 else:
591 else:
553 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
592 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
554
593
555 def label(self, msg, label):
594 def label(self, msg, label):
556 '''style msg based on supplied label
595 '''style msg based on supplied label
557
596
558 Like ui.write(), this just returns msg unchanged, but extensions
597 Like ui.write(), this just returns msg unchanged, but extensions
559 and GUI tools can override it to allow styling output without
598 and GUI tools can override it to allow styling output without
560 writing it.
599 writing it.
561
600
562 ui.write(s, 'label') is equivalent to
601 ui.write(s, 'label') is equivalent to
563 ui.write(ui.label(s, 'label')).
602 ui.write(ui.label(s, 'label')).
564 '''
603 '''
565 return msg
604 return msg
General Comments 0
You need to be logged in to leave comments. Login now