##// END OF EJS Templates
ui: add ui.formatted configuration variable and accessor function....
Dan Villiom Podlaski Christiansen -
r11324:cdf6d861 default
parent child Browse files
Show More
@@ -1,550 +1,565
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 return 'HGPLAIN' in os.environ
256 return 'HGPLAIN' in os.environ
257
257
258 def username(self):
258 def username(self):
259 """Return default username to be used in commits.
259 """Return default username to be used in commits.
260
260
261 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
261 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
262 and stop searching if one of these is set.
262 and stop searching if one of these is set.
263 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
264 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
264 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
265 """
265 """
266 user = os.environ.get("HGUSER")
266 user = os.environ.get("HGUSER")
267 if user is None:
267 if user is None:
268 user = self.config("ui", "username")
268 user = self.config("ui", "username")
269 if user is not None:
269 if user is not None:
270 user = os.path.expandvars(user)
270 user = os.path.expandvars(user)
271 if user is None:
271 if user is None:
272 user = os.environ.get("EMAIL")
272 user = os.environ.get("EMAIL")
273 if user is None and self.configbool("ui", "askusername"):
273 if user is None and self.configbool("ui", "askusername"):
274 user = self.prompt(_("enter a commit username:"), default=None)
274 user = self.prompt(_("enter a commit username:"), default=None)
275 if user is None and not self.interactive():
275 if user is None and not self.interactive():
276 try:
276 try:
277 user = '%s@%s' % (util.getuser(), socket.getfqdn())
277 user = '%s@%s' % (util.getuser(), socket.getfqdn())
278 self.warn(_("No username found, using '%s' instead\n") % user)
278 self.warn(_("No username found, using '%s' instead\n") % user)
279 except KeyError:
279 except KeyError:
280 pass
280 pass
281 if not user:
281 if not user:
282 raise util.Abort(_('no username supplied (see "hg help config")'))
282 raise util.Abort(_('no username supplied (see "hg help config")'))
283 if "\n" in user:
283 if "\n" in user:
284 raise util.Abort(_("username %s contains a newline\n") % repr(user))
284 raise util.Abort(_("username %s contains a newline\n") % repr(user))
285 return user
285 return user
286
286
287 def shortuser(self, user):
287 def shortuser(self, user):
288 """Return a short representation of a user name or email address."""
288 """Return a short representation of a user name or email address."""
289 if not self.verbose:
289 if not self.verbose:
290 user = util.shortuser(user)
290 user = util.shortuser(user)
291 return user
291 return user
292
292
293 def _path(self, loc):
293 def _path(self, loc):
294 p = self.config('paths', loc)
294 p = self.config('paths', loc)
295 if p:
295 if p:
296 if '%%' in p:
296 if '%%' in p:
297 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
297 self.warn("(deprecated '%%' in path %s=%s from %s)\n" %
298 (loc, p, self.configsource('paths', loc)))
298 (loc, p, self.configsource('paths', loc)))
299 p = p.replace('%%', '%')
299 p = p.replace('%%', '%')
300 p = util.expandpath(p)
300 p = util.expandpath(p)
301 return p
301 return p
302
302
303 def expandpath(self, loc, default=None):
303 def expandpath(self, loc, default=None):
304 """Return repository location relative to cwd or from [paths]"""
304 """Return repository location relative to cwd or from [paths]"""
305 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
305 if "://" in loc or os.path.isdir(os.path.join(loc, '.hg')):
306 return loc
306 return loc
307
307
308 path = self._path(loc)
308 path = self._path(loc)
309 if not path and default is not None:
309 if not path and default is not None:
310 path = self._path(default)
310 path = self._path(default)
311 return path or loc
311 return path or loc
312
312
313 def pushbuffer(self):
313 def pushbuffer(self):
314 self._buffers.append([])
314 self._buffers.append([])
315
315
316 def popbuffer(self, labeled=False):
316 def popbuffer(self, labeled=False):
317 '''pop the last buffer and return the buffered output
317 '''pop the last buffer and return the buffered output
318
318
319 If labeled is True, any labels associated with buffered
319 If labeled is True, any labels associated with buffered
320 output will be handled. By default, this has no effect
320 output will be handled. By default, this has no effect
321 on the output returned, but extensions and GUI tools may
321 on the output returned, but extensions and GUI tools may
322 handle this argument and returned styled output. If output
322 handle this argument and returned styled output. If output
323 is being buffered so it can be captured and parsed or
323 is being buffered so it can be captured and parsed or
324 processed, labeled should not be set to True.
324 processed, labeled should not be set to True.
325 '''
325 '''
326 return "".join(self._buffers.pop())
326 return "".join(self._buffers.pop())
327
327
328 def write(self, *args, **opts):
328 def write(self, *args, **opts):
329 '''write args to output
329 '''write args to output
330
330
331 By default, this method simply writes to the buffer or stdout,
331 By default, this method simply writes to the buffer or stdout,
332 but extensions or GUI tools may override this method,
332 but extensions or GUI tools may override this method,
333 write_err(), popbuffer(), and label() to style output from
333 write_err(), popbuffer(), and label() to style output from
334 various parts of hg.
334 various parts of hg.
335
335
336 An optional keyword argument, "label", can be passed in.
336 An optional keyword argument, "label", can be passed in.
337 This should be a string containing label names separated by
337 This should be a string containing label names separated by
338 space. Label names take the form of "topic.type". For example,
338 space. Label names take the form of "topic.type". For example,
339 ui.debug() issues a label of "ui.debug".
339 ui.debug() issues a label of "ui.debug".
340
340
341 When labeling output for a specific command, a label of
341 When labeling output for a specific command, a label of
342 "cmdname.type" is recommended. For example, status issues
342 "cmdname.type" is recommended. For example, status issues
343 a label of "status.modified" for modified files.
343 a label of "status.modified" for modified files.
344 '''
344 '''
345 if self._buffers:
345 if self._buffers:
346 self._buffers[-1].extend([str(a) for a in args])
346 self._buffers[-1].extend([str(a) for a in args])
347 else:
347 else:
348 for a in args:
348 for a in args:
349 sys.stdout.write(str(a))
349 sys.stdout.write(str(a))
350
350
351 def write_err(self, *args, **opts):
351 def write_err(self, *args, **opts):
352 try:
352 try:
353 if not getattr(sys.stdout, 'closed', False):
353 if not getattr(sys.stdout, 'closed', False):
354 sys.stdout.flush()
354 sys.stdout.flush()
355 for a in args:
355 for a in args:
356 sys.stderr.write(str(a))
356 sys.stderr.write(str(a))
357 # stderr may be buffered under win32 when redirected to files,
357 # stderr may be buffered under win32 when redirected to files,
358 # including stdout.
358 # including stdout.
359 if not getattr(sys.stderr, 'closed', False):
359 if not getattr(sys.stderr, 'closed', False):
360 sys.stderr.flush()
360 sys.stderr.flush()
361 except IOError, inst:
361 except IOError, inst:
362 if inst.errno != errno.EPIPE:
362 if inst.errno != errno.EPIPE:
363 raise
363 raise
364
364
365 def flush(self):
365 def flush(self):
366 try: sys.stdout.flush()
366 try: sys.stdout.flush()
367 except: pass
367 except: pass
368 try: sys.stderr.flush()
368 try: sys.stderr.flush()
369 except: pass
369 except: pass
370
370
371 def interactive(self):
371 def interactive(self):
372 i = self.configbool("ui", "interactive", None)
372 i = self.configbool("ui", "interactive", None)
373 if i is None:
373 if i is None:
374 try:
374 try:
375 return sys.stdin.isatty()
375 return sys.stdin.isatty()
376 except AttributeError:
376 except AttributeError:
377 # some environments replace stdin without implementing isatty
377 # some environments replace stdin without implementing isatty
378 # usually those are non-interactive
378 # usually those are non-interactive
379 return False
379 return False
380
380
381 return i
381 return i
382
382
383 def formatted(self):
384 if self.plain():
385 return False
386
387 i = self.configbool("ui", "formatted", None)
388 if i is None:
389 try:
390 return sys.stdout.isatty()
391 except AttributeError:
392 # some environments replace stdout without implementing isatty
393 # usually those are non-interactive
394 return False
395
396 return i
397
383 def _readline(self, prompt=''):
398 def _readline(self, prompt=''):
384 if sys.stdin.isatty():
399 if sys.stdin.isatty():
385 try:
400 try:
386 # magically add command line editing support, where
401 # magically add command line editing support, where
387 # available
402 # available
388 import readline
403 import readline
389 # force demandimport to really load the module
404 # force demandimport to really load the module
390 readline.read_history_file
405 readline.read_history_file
391 # windows sometimes raises something other than ImportError
406 # windows sometimes raises something other than ImportError
392 except Exception:
407 except Exception:
393 pass
408 pass
394 line = raw_input(prompt)
409 line = raw_input(prompt)
395 # When stdin is in binary mode on Windows, it can cause
410 # When stdin is in binary mode on Windows, it can cause
396 # raw_input() to emit an extra trailing carriage return
411 # raw_input() to emit an extra trailing carriage return
397 if os.linesep == '\r\n' and line and line[-1] == '\r':
412 if os.linesep == '\r\n' and line and line[-1] == '\r':
398 line = line[:-1]
413 line = line[:-1]
399 return line
414 return line
400
415
401 def prompt(self, msg, default="y"):
416 def prompt(self, msg, default="y"):
402 """Prompt user with msg, read response.
417 """Prompt user with msg, read response.
403 If ui is not interactive, the default is returned.
418 If ui is not interactive, the default is returned.
404 """
419 """
405 if not self.interactive():
420 if not self.interactive():
406 self.write(msg, ' ', default, "\n")
421 self.write(msg, ' ', default, "\n")
407 return default
422 return default
408 try:
423 try:
409 r = self._readline(msg + ' ')
424 r = self._readline(msg + ' ')
410 if not r:
425 if not r:
411 return default
426 return default
412 return r
427 return r
413 except EOFError:
428 except EOFError:
414 raise util.Abort(_('response expected'))
429 raise util.Abort(_('response expected'))
415
430
416 def promptchoice(self, msg, choices, default=0):
431 def promptchoice(self, msg, choices, default=0):
417 """Prompt user with msg, read response, and ensure it matches
432 """Prompt user with msg, read response, and ensure it matches
418 one of the provided choices. The index of the choice is returned.
433 one of the provided choices. The index of the choice is returned.
419 choices is a sequence of acceptable responses with the format:
434 choices is a sequence of acceptable responses with the format:
420 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
435 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
421 If ui is not interactive, the default is returned.
436 If ui is not interactive, the default is returned.
422 """
437 """
423 resps = [s[s.index('&')+1].lower() for s in choices]
438 resps = [s[s.index('&')+1].lower() for s in choices]
424 while True:
439 while True:
425 r = self.prompt(msg, resps[default])
440 r = self.prompt(msg, resps[default])
426 if r.lower() in resps:
441 if r.lower() in resps:
427 return resps.index(r.lower())
442 return resps.index(r.lower())
428 self.write(_("unrecognized response\n"))
443 self.write(_("unrecognized response\n"))
429
444
430 def getpass(self, prompt=None, default=None):
445 def getpass(self, prompt=None, default=None):
431 if not self.interactive():
446 if not self.interactive():
432 return default
447 return default
433 try:
448 try:
434 return getpass.getpass(prompt or _('password: '))
449 return getpass.getpass(prompt or _('password: '))
435 except EOFError:
450 except EOFError:
436 raise util.Abort(_('response expected'))
451 raise util.Abort(_('response expected'))
437 def status(self, *msg, **opts):
452 def status(self, *msg, **opts):
438 '''write status message to output (if ui.quiet is False)
453 '''write status message to output (if ui.quiet is False)
439
454
440 This adds an output label of "ui.status".
455 This adds an output label of "ui.status".
441 '''
456 '''
442 if not self.quiet:
457 if not self.quiet:
443 opts['label'] = opts.get('label', '') + ' ui.status'
458 opts['label'] = opts.get('label', '') + ' ui.status'
444 self.write(*msg, **opts)
459 self.write(*msg, **opts)
445 def warn(self, *msg, **opts):
460 def warn(self, *msg, **opts):
446 '''write warning message to output (stderr)
461 '''write warning message to output (stderr)
447
462
448 This adds an output label of "ui.warning".
463 This adds an output label of "ui.warning".
449 '''
464 '''
450 opts['label'] = opts.get('label', '') + ' ui.warning'
465 opts['label'] = opts.get('label', '') + ' ui.warning'
451 self.write_err(*msg, **opts)
466 self.write_err(*msg, **opts)
452 def note(self, *msg, **opts):
467 def note(self, *msg, **opts):
453 '''write note to output (if ui.verbose is True)
468 '''write note to output (if ui.verbose is True)
454
469
455 This adds an output label of "ui.note".
470 This adds an output label of "ui.note".
456 '''
471 '''
457 if self.verbose:
472 if self.verbose:
458 opts['label'] = opts.get('label', '') + ' ui.note'
473 opts['label'] = opts.get('label', '') + ' ui.note'
459 self.write(*msg, **opts)
474 self.write(*msg, **opts)
460 def debug(self, *msg, **opts):
475 def debug(self, *msg, **opts):
461 '''write debug message to output (if ui.debugflag is True)
476 '''write debug message to output (if ui.debugflag is True)
462
477
463 This adds an output label of "ui.debug".
478 This adds an output label of "ui.debug".
464 '''
479 '''
465 if self.debugflag:
480 if self.debugflag:
466 opts['label'] = opts.get('label', '') + ' ui.debug'
481 opts['label'] = opts.get('label', '') + ' ui.debug'
467 self.write(*msg, **opts)
482 self.write(*msg, **opts)
468 def edit(self, text, user):
483 def edit(self, text, user):
469 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
484 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
470 text=True)
485 text=True)
471 try:
486 try:
472 f = os.fdopen(fd, "w")
487 f = os.fdopen(fd, "w")
473 f.write(text)
488 f.write(text)
474 f.close()
489 f.close()
475
490
476 editor = self.geteditor()
491 editor = self.geteditor()
477
492
478 util.system("%s \"%s\"" % (editor, name),
493 util.system("%s \"%s\"" % (editor, name),
479 environ={'HGUSER': user},
494 environ={'HGUSER': user},
480 onerr=util.Abort, errprefix=_("edit failed"))
495 onerr=util.Abort, errprefix=_("edit failed"))
481
496
482 f = open(name)
497 f = open(name)
483 t = f.read()
498 t = f.read()
484 f.close()
499 f.close()
485 finally:
500 finally:
486 os.unlink(name)
501 os.unlink(name)
487
502
488 return t
503 return t
489
504
490 def traceback(self, exc=None):
505 def traceback(self, exc=None):
491 '''print exception traceback if traceback printing enabled.
506 '''print exception traceback if traceback printing enabled.
492 only to call in exception handler. returns true if traceback
507 only to call in exception handler. returns true if traceback
493 printed.'''
508 printed.'''
494 if self.tracebackflag:
509 if self.tracebackflag:
495 if exc:
510 if exc:
496 traceback.print_exception(exc[0], exc[1], exc[2])
511 traceback.print_exception(exc[0], exc[1], exc[2])
497 else:
512 else:
498 traceback.print_exc()
513 traceback.print_exc()
499 return self.tracebackflag
514 return self.tracebackflag
500
515
501 def geteditor(self):
516 def geteditor(self):
502 '''return editor to use'''
517 '''return editor to use'''
503 return (os.environ.get("HGEDITOR") or
518 return (os.environ.get("HGEDITOR") or
504 self.config("ui", "editor") or
519 self.config("ui", "editor") or
505 os.environ.get("VISUAL") or
520 os.environ.get("VISUAL") or
506 os.environ.get("EDITOR", "vi"))
521 os.environ.get("EDITOR", "vi"))
507
522
508 def progress(self, topic, pos, item="", unit="", total=None):
523 def progress(self, topic, pos, item="", unit="", total=None):
509 '''show a progress message
524 '''show a progress message
510
525
511 With stock hg, this is simply a debug message that is hidden
526 With stock hg, this is simply a debug message that is hidden
512 by default, but with extensions or GUI tools it may be
527 by default, but with extensions or GUI tools it may be
513 visible. 'topic' is the current operation, 'item' is a
528 visible. 'topic' is the current operation, 'item' is a
514 non-numeric marker of the current position (ie the currently
529 non-numeric marker of the current position (ie the currently
515 in-process file), 'pos' is the current numeric position (ie
530 in-process file), 'pos' is the current numeric position (ie
516 revision, bytes, etc.), unit is a corresponding unit label,
531 revision, bytes, etc.), unit is a corresponding unit label,
517 and total is the highest expected pos.
532 and total is the highest expected pos.
518
533
519 Multiple nested topics may be active at a time.
534 Multiple nested topics may be active at a time.
520
535
521 All topics should be marked closed by setting pos to None at
536 All topics should be marked closed by setting pos to None at
522 termination.
537 termination.
523 '''
538 '''
524
539
525 if pos == None or not self.debugflag:
540 if pos == None or not self.debugflag:
526 return
541 return
527
542
528 if unit:
543 if unit:
529 unit = ' ' + unit
544 unit = ' ' + unit
530 if item:
545 if item:
531 item = ' ' + item
546 item = ' ' + item
532
547
533 if total:
548 if total:
534 pct = 100.0 * pos / total
549 pct = 100.0 * pos / total
535 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
550 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
536 % (topic, item, pos, total, unit, pct))
551 % (topic, item, pos, total, unit, pct))
537 else:
552 else:
538 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
553 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
539
554
540 def label(self, msg, label):
555 def label(self, msg, label):
541 '''style msg based on supplied label
556 '''style msg based on supplied label
542
557
543 Like ui.write(), this just returns msg unchanged, but extensions
558 Like ui.write(), this just returns msg unchanged, but extensions
544 and GUI tools can override it to allow styling output without
559 and GUI tools can override it to allow styling output without
545 writing it.
560 writing it.
546
561
547 ui.write(s, 'label') is equivalent to
562 ui.write(s, 'label') is equivalent to
548 ui.write(ui.label(s, 'label')).
563 ui.write(ui.label(s, 'label')).
549 '''
564 '''
550 return msg
565 return msg
General Comments 0
You need to be logged in to leave comments. Login now