##// END OF EJS Templates
ui: add configint function and tests
Sune Foldager -
r14171:fa2b596d default
parent child Browse files
Show More
@@ -1,643 +1,698 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, scmutil, util, error
10 import config, scmutil, util, error
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22
22
23 if src:
23 if src:
24 self._tcfg = src._tcfg.copy()
24 self._tcfg = src._tcfg.copy()
25 self._ucfg = src._ucfg.copy()
25 self._ucfg = src._ucfg.copy()
26 self._ocfg = src._ocfg.copy()
26 self._ocfg = src._ocfg.copy()
27 self._trustusers = src._trustusers.copy()
27 self._trustusers = src._trustusers.copy()
28 self._trustgroups = src._trustgroups.copy()
28 self._trustgroups = src._trustgroups.copy()
29 self.environ = src.environ
29 self.environ = src.environ
30 self.fixconfig()
30 self.fixconfig()
31 else:
31 else:
32 # shared read-only environment
32 # shared read-only environment
33 self.environ = os.environ
33 self.environ = os.environ
34 # we always trust global config files
34 # we always trust global config files
35 for f in scmutil.rcpath():
35 for f in scmutil.rcpath():
36 self.readconfig(f, trust=True)
36 self.readconfig(f, trust=True)
37
37
38 def copy(self):
38 def copy(self):
39 return self.__class__(self)
39 return self.__class__(self)
40
40
41 def _is_trusted(self, fp, f):
41 def _is_trusted(self, fp, f):
42 st = util.fstat(fp)
42 st = util.fstat(fp)
43 if util.isowner(st):
43 if util.isowner(st):
44 return True
44 return True
45
45
46 tusers, tgroups = self._trustusers, self._trustgroups
46 tusers, tgroups = self._trustusers, self._trustgroups
47 if '*' in tusers or '*' in tgroups:
47 if '*' in tusers or '*' in tgroups:
48 return True
48 return True
49
49
50 user = util.username(st.st_uid)
50 user = util.username(st.st_uid)
51 group = util.groupname(st.st_gid)
51 group = util.groupname(st.st_gid)
52 if user in tusers or group in tgroups or user == util.username():
52 if user in tusers or group in tgroups or user == util.username():
53 return True
53 return True
54
54
55 if self._reportuntrusted:
55 if self._reportuntrusted:
56 self.warn(_('Not trusting file %s from untrusted '
56 self.warn(_('Not trusting file %s from untrusted '
57 'user %s, group %s\n') % (f, user, group))
57 'user %s, group %s\n') % (f, user, group))
58 return False
58 return False
59
59
60 def readconfig(self, filename, root=None, trust=False,
60 def readconfig(self, filename, root=None, trust=False,
61 sections=None, remap=None):
61 sections=None, remap=None):
62 try:
62 try:
63 fp = open(filename)
63 fp = open(filename)
64 except IOError:
64 except IOError:
65 if not sections: # ignore unless we were looking for something
65 if not sections: # ignore unless we were looking for something
66 return
66 return
67 raise
67 raise
68
68
69 cfg = config.config()
69 cfg = config.config()
70 trusted = sections or trust or self._is_trusted(fp, filename)
70 trusted = sections or trust or self._is_trusted(fp, filename)
71
71
72 try:
72 try:
73 cfg.read(filename, fp, sections=sections, remap=remap)
73 cfg.read(filename, fp, sections=sections, remap=remap)
74 except error.ConfigError, inst:
74 except error.ConfigError, inst:
75 if trusted:
75 if trusted:
76 raise
76 raise
77 self.warn(_("Ignored: %s\n") % str(inst))
77 self.warn(_("Ignored: %s\n") % str(inst))
78
78
79 if self.plain():
79 if self.plain():
80 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
80 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
81 'logtemplate', 'style',
81 'logtemplate', 'style',
82 'traceback', 'verbose'):
82 'traceback', 'verbose'):
83 if k in cfg['ui']:
83 if k in cfg['ui']:
84 del cfg['ui'][k]
84 del cfg['ui'][k]
85 for k, v in cfg.items('alias'):
85 for k, v in cfg.items('alias'):
86 del cfg['alias'][k]
86 del cfg['alias'][k]
87 for k, v in cfg.items('defaults'):
87 for k, v in cfg.items('defaults'):
88 del cfg['defaults'][k]
88 del cfg['defaults'][k]
89
89
90 if trusted:
90 if trusted:
91 self._tcfg.update(cfg)
91 self._tcfg.update(cfg)
92 self._tcfg.update(self._ocfg)
92 self._tcfg.update(self._ocfg)
93 self._ucfg.update(cfg)
93 self._ucfg.update(cfg)
94 self._ucfg.update(self._ocfg)
94 self._ucfg.update(self._ocfg)
95
95
96 if root is None:
96 if root is None:
97 root = os.path.expanduser('~')
97 root = os.path.expanduser('~')
98 self.fixconfig(root=root)
98 self.fixconfig(root=root)
99
99
100 def fixconfig(self, root=None, section=None):
100 def fixconfig(self, root=None, section=None):
101 if section in (None, 'paths'):
101 if section in (None, 'paths'):
102 # expand vars and ~
102 # expand vars and ~
103 # translate paths relative to root (or home) into absolute paths
103 # translate paths relative to root (or home) into absolute paths
104 root = root or os.getcwd()
104 root = root or os.getcwd()
105 for c in self._tcfg, self._ucfg, self._ocfg:
105 for c in self._tcfg, self._ucfg, self._ocfg:
106 for n, p in c.items('paths'):
106 for n, p in c.items('paths'):
107 if not p:
107 if not p:
108 continue
108 continue
109 if '%%' in p:
109 if '%%' in p:
110 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
110 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
111 % (n, p, self.configsource('paths', n)))
111 % (n, p, self.configsource('paths', n)))
112 p = p.replace('%%', '%')
112 p = p.replace('%%', '%')
113 p = util.expandpath(p)
113 p = util.expandpath(p)
114 if not util.hasscheme(p) and not os.path.isabs(p):
114 if not util.hasscheme(p) and not os.path.isabs(p):
115 p = os.path.normpath(os.path.join(root, p))
115 p = os.path.normpath(os.path.join(root, p))
116 c.set("paths", n, p)
116 c.set("paths", n, p)
117
117
118 if section in (None, 'ui'):
118 if section in (None, 'ui'):
119 # update ui options
119 # update ui options
120 self.debugflag = self.configbool('ui', 'debug')
120 self.debugflag = self.configbool('ui', 'debug')
121 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
121 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
122 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
122 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
123 if self.verbose and self.quiet:
123 if self.verbose and self.quiet:
124 self.quiet = self.verbose = False
124 self.quiet = self.verbose = False
125 self._reportuntrusted = self.debugflag or self.configbool("ui",
125 self._reportuntrusted = self.debugflag or self.configbool("ui",
126 "report_untrusted", True)
126 "report_untrusted", True)
127 self.tracebackflag = self.configbool('ui', 'traceback', False)
127 self.tracebackflag = self.configbool('ui', 'traceback', False)
128
128
129 if section in (None, 'trusted'):
129 if section in (None, 'trusted'):
130 # update trust information
130 # update trust information
131 self._trustusers.update(self.configlist('trusted', 'users'))
131 self._trustusers.update(self.configlist('trusted', 'users'))
132 self._trustgroups.update(self.configlist('trusted', 'groups'))
132 self._trustgroups.update(self.configlist('trusted', 'groups'))
133
133
134 def setconfig(self, section, name, value, overlay=True):
134 def setconfig(self, section, name, value, overlay=True):
135 if overlay:
135 if overlay:
136 self._ocfg.set(section, name, value)
136 self._ocfg.set(section, name, value)
137 self._tcfg.set(section, name, value)
137 self._tcfg.set(section, name, value)
138 self._ucfg.set(section, name, value)
138 self._ucfg.set(section, name, value)
139 self.fixconfig(section=section)
139 self.fixconfig(section=section)
140
140
141 def _data(self, untrusted):
141 def _data(self, untrusted):
142 return untrusted and self._ucfg or self._tcfg
142 return untrusted and self._ucfg or self._tcfg
143
143
144 def configsource(self, section, name, untrusted=False):
144 def configsource(self, section, name, untrusted=False):
145 return self._data(untrusted).source(section, name) or 'none'
145 return self._data(untrusted).source(section, name) or 'none'
146
146
147 def config(self, section, name, default=None, untrusted=False):
147 def config(self, section, name, default=None, untrusted=False):
148 value = self._data(untrusted).get(section, name, default)
148 value = self._data(untrusted).get(section, name, default)
149 if self.debugflag and not untrusted and self._reportuntrusted:
149 if self.debugflag and not untrusted and self._reportuntrusted:
150 uvalue = self._ucfg.get(section, name)
150 uvalue = self._ucfg.get(section, name)
151 if uvalue is not None and uvalue != value:
151 if uvalue is not None and uvalue != value:
152 self.debug(_("ignoring untrusted configuration option "
152 self.debug(_("ignoring untrusted configuration option "
153 "%s.%s = %s\n") % (section, name, uvalue))
153 "%s.%s = %s\n") % (section, name, uvalue))
154 return value
154 return value
155
155
156 def configpath(self, section, name, default=None, untrusted=False):
156 def configpath(self, section, name, default=None, untrusted=False):
157 'get a path config item, expanded relative to config file'
157 'get a path config item, expanded relative to config file'
158 v = self.config(section, name, default, untrusted)
158 v = self.config(section, name, default, untrusted)
159 if not os.path.isabs(v) or "://" not in v:
159 if not os.path.isabs(v) or "://" not in v:
160 src = self.configsource(section, name, untrusted)
160 src = self.configsource(section, name, untrusted)
161 if ':' in src:
161 if ':' in src:
162 base = os.path.dirname(src.rsplit(':'))
162 base = os.path.dirname(src.rsplit(':'))
163 v = os.path.join(base, os.path.expanduser(v))
163 v = os.path.join(base, os.path.expanduser(v))
164 return v
164 return v
165
165
166 def configbool(self, section, name, default=False, untrusted=False):
166 def configbool(self, section, name, default=False, untrusted=False):
167 """parse a configuration element as a boolean
168
169 >>> u = ui(); s = 'foo'
170 >>> u.setconfig(s, 'true', 'yes')
171 >>> u.configbool(s, 'true')
172 True
173 >>> u.setconfig(s, 'false', 'no')
174 >>> u.configbool(s, 'false')
175 False
176 >>> u.configbool(s, 'unknown')
177 False
178 >>> u.configbool(s, 'unknown', True)
179 True
180 >>> u.setconfig(s, 'invalid', 'somevalue')
181 >>> u.configbool(s, 'invalid')
182 Traceback (most recent call last):
183 ...
184 ConfigError: foo.invalid is not a boolean ('somevalue')
185 """
186
167 v = self.config(section, name, None, untrusted)
187 v = self.config(section, name, None, untrusted)
168 if v is None:
188 if v is None:
169 return default
189 return default
170 if isinstance(v, bool):
190 if isinstance(v, bool):
171 return v
191 return v
172 b = util.parsebool(v)
192 b = util.parsebool(v)
173 if b is None:
193 if b is None:
174 raise error.ConfigError(_("%s.%s not a boolean ('%s')")
194 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
175 % (section, name, v))
195 % (section, name, v))
176 return b
196 return b
177
197
198 def configint(self, section, name, default=None, untrusted=False):
199 """parse a configuration element as an integer
200
201 >>> u = ui(); s = 'foo'
202 >>> u.setconfig(s, 'int1', '42')
203 >>> u.configint(s, 'int1')
204 42
205 >>> u.setconfig(s, 'int2', '-42')
206 >>> u.configint(s, 'int2')
207 -42
208 >>> u.configint(s, 'unknown', 7)
209 7
210 >>> u.setconfig(s, 'invalid', 'somevalue')
211 >>> u.configint(s, 'invalid')
212 Traceback (most recent call last):
213 ...
214 ConfigError: foo.invalid is not an integer ('somevalue')
215 """
216
217 v = self.config(section, name, None, untrusted)
218 if v is None:
219 return default
220 try:
221 return int(v)
222 except ValueError:
223 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
224 % (section, name, v))
225
178 def configlist(self, section, name, default=None, untrusted=False):
226 def configlist(self, section, name, default=None, untrusted=False):
179 """Return a list of comma/space separated strings"""
227 """parse a configuration element as a list of comma/space separated
228 strings
229
230 >>> u = ui(); s = 'foo'
231 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
232 >>> u.configlist(s, 'list1')
233 ['this', 'is', 'a small', 'test']
234 """
180
235
181 def _parse_plain(parts, s, offset):
236 def _parse_plain(parts, s, offset):
182 whitespace = False
237 whitespace = False
183 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
238 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
184 whitespace = True
239 whitespace = True
185 offset += 1
240 offset += 1
186 if offset >= len(s):
241 if offset >= len(s):
187 return None, parts, offset
242 return None, parts, offset
188 if whitespace:
243 if whitespace:
189 parts.append('')
244 parts.append('')
190 if s[offset] == '"' and not parts[-1]:
245 if s[offset] == '"' and not parts[-1]:
191 return _parse_quote, parts, offset + 1
246 return _parse_quote, parts, offset + 1
192 elif s[offset] == '"' and parts[-1][-1] == '\\':
247 elif s[offset] == '"' and parts[-1][-1] == '\\':
193 parts[-1] = parts[-1][:-1] + s[offset]
248 parts[-1] = parts[-1][:-1] + s[offset]
194 return _parse_plain, parts, offset + 1
249 return _parse_plain, parts, offset + 1
195 parts[-1] += s[offset]
250 parts[-1] += s[offset]
196 return _parse_plain, parts, offset + 1
251 return _parse_plain, parts, offset + 1
197
252
198 def _parse_quote(parts, s, offset):
253 def _parse_quote(parts, s, offset):
199 if offset < len(s) and s[offset] == '"': # ""
254 if offset < len(s) and s[offset] == '"': # ""
200 parts.append('')
255 parts.append('')
201 offset += 1
256 offset += 1
202 while offset < len(s) and (s[offset].isspace() or
257 while offset < len(s) and (s[offset].isspace() or
203 s[offset] == ','):
258 s[offset] == ','):
204 offset += 1
259 offset += 1
205 return _parse_plain, parts, offset
260 return _parse_plain, parts, offset
206
261
207 while offset < len(s) and s[offset] != '"':
262 while offset < len(s) and s[offset] != '"':
208 if (s[offset] == '\\' and offset + 1 < len(s)
263 if (s[offset] == '\\' and offset + 1 < len(s)
209 and s[offset + 1] == '"'):
264 and s[offset + 1] == '"'):
210 offset += 1
265 offset += 1
211 parts[-1] += '"'
266 parts[-1] += '"'
212 else:
267 else:
213 parts[-1] += s[offset]
268 parts[-1] += s[offset]
214 offset += 1
269 offset += 1
215
270
216 if offset >= len(s):
271 if offset >= len(s):
217 real_parts = _configlist(parts[-1])
272 real_parts = _configlist(parts[-1])
218 if not real_parts:
273 if not real_parts:
219 parts[-1] = '"'
274 parts[-1] = '"'
220 else:
275 else:
221 real_parts[0] = '"' + real_parts[0]
276 real_parts[0] = '"' + real_parts[0]
222 parts = parts[:-1]
277 parts = parts[:-1]
223 parts.extend(real_parts)
278 parts.extend(real_parts)
224 return None, parts, offset
279 return None, parts, offset
225
280
226 offset += 1
281 offset += 1
227 while offset < len(s) and s[offset] in [' ', ',']:
282 while offset < len(s) and s[offset] in [' ', ',']:
228 offset += 1
283 offset += 1
229
284
230 if offset < len(s):
285 if offset < len(s):
231 if offset + 1 == len(s) and s[offset] == '"':
286 if offset + 1 == len(s) and s[offset] == '"':
232 parts[-1] += '"'
287 parts[-1] += '"'
233 offset += 1
288 offset += 1
234 else:
289 else:
235 parts.append('')
290 parts.append('')
236 else:
291 else:
237 return None, parts, offset
292 return None, parts, offset
238
293
239 return _parse_plain, parts, offset
294 return _parse_plain, parts, offset
240
295
241 def _configlist(s):
296 def _configlist(s):
242 s = s.rstrip(' ,')
297 s = s.rstrip(' ,')
243 if not s:
298 if not s:
244 return []
299 return []
245 parser, parts, offset = _parse_plain, [''], 0
300 parser, parts, offset = _parse_plain, [''], 0
246 while parser:
301 while parser:
247 parser, parts, offset = parser(parts, s, offset)
302 parser, parts, offset = parser(parts, s, offset)
248 return parts
303 return parts
249
304
250 result = self.config(section, name, untrusted=untrusted)
305 result = self.config(section, name, untrusted=untrusted)
251 if result is None:
306 if result is None:
252 result = default or []
307 result = default or []
253 if isinstance(result, basestring):
308 if isinstance(result, basestring):
254 result = _configlist(result.lstrip(' ,\n'))
309 result = _configlist(result.lstrip(' ,\n'))
255 if result is None:
310 if result is None:
256 result = default or []
311 result = default or []
257 return result
312 return result
258
313
259 def has_section(self, section, untrusted=False):
314 def has_section(self, section, untrusted=False):
260 '''tell whether section exists in config.'''
315 '''tell whether section exists in config.'''
261 return section in self._data(untrusted)
316 return section in self._data(untrusted)
262
317
263 def configitems(self, section, untrusted=False):
318 def configitems(self, section, untrusted=False):
264 items = self._data(untrusted).items(section)
319 items = self._data(untrusted).items(section)
265 if self.debugflag and not untrusted and self._reportuntrusted:
320 if self.debugflag and not untrusted and self._reportuntrusted:
266 for k, v in self._ucfg.items(section):
321 for k, v in self._ucfg.items(section):
267 if self._tcfg.get(section, k) != v:
322 if self._tcfg.get(section, k) != v:
268 self.debug(_("ignoring untrusted configuration option "
323 self.debug(_("ignoring untrusted configuration option "
269 "%s.%s = %s\n") % (section, k, v))
324 "%s.%s = %s\n") % (section, k, v))
270 return items
325 return items
271
326
272 def walkconfig(self, untrusted=False):
327 def walkconfig(self, untrusted=False):
273 cfg = self._data(untrusted)
328 cfg = self._data(untrusted)
274 for section in cfg.sections():
329 for section in cfg.sections():
275 for name, value in self.configitems(section, untrusted):
330 for name, value in self.configitems(section, untrusted):
276 yield section, name, value
331 yield section, name, value
277
332
278 def plain(self):
333 def plain(self):
279 '''is plain mode active?
334 '''is plain mode active?
280
335
281 Plain mode means that all configuration variables which affect
336 Plain mode means that all configuration variables which affect
282 the behavior and output of Mercurial should be
337 the behavior and output of Mercurial should be
283 ignored. Additionally, the output should be stable,
338 ignored. Additionally, the output should be stable,
284 reproducible and suitable for use in scripts or applications.
339 reproducible and suitable for use in scripts or applications.
285
340
286 The only way to trigger plain mode is by setting either the
341 The only way to trigger plain mode is by setting either the
287 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
342 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
288
343
289 The return value can either be False, True, or a list of
344 The return value can either be False, True, or a list of
290 features that plain mode should not apply to (e.g., i18n,
345 features that plain mode should not apply to (e.g., i18n,
291 progress, etc).
346 progress, etc).
292 '''
347 '''
293 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
348 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
294 return False
349 return False
295 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
350 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
296 return exceptions or True
351 return exceptions or True
297
352
298 def username(self):
353 def username(self):
299 """Return default username to be used in commits.
354 """Return default username to be used in commits.
300
355
301 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
356 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
302 and stop searching if one of these is set.
357 and stop searching if one of these is set.
303 If not found and ui.askusername is True, ask the user, else use
358 If not found and ui.askusername is True, ask the user, else use
304 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
359 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
305 """
360 """
306 user = os.environ.get("HGUSER")
361 user = os.environ.get("HGUSER")
307 if user is None:
362 if user is None:
308 user = self.config("ui", "username")
363 user = self.config("ui", "username")
309 if user is not None:
364 if user is not None:
310 user = os.path.expandvars(user)
365 user = os.path.expandvars(user)
311 if user is None:
366 if user is None:
312 user = os.environ.get("EMAIL")
367 user = os.environ.get("EMAIL")
313 if user is None and self.configbool("ui", "askusername"):
368 if user is None and self.configbool("ui", "askusername"):
314 user = self.prompt(_("enter a commit username:"), default=None)
369 user = self.prompt(_("enter a commit username:"), default=None)
315 if user is None and not self.interactive():
370 if user is None and not self.interactive():
316 try:
371 try:
317 user = '%s@%s' % (util.getuser(), socket.getfqdn())
372 user = '%s@%s' % (util.getuser(), socket.getfqdn())
318 self.warn(_("No username found, using '%s' instead\n") % user)
373 self.warn(_("No username found, using '%s' instead\n") % user)
319 except KeyError:
374 except KeyError:
320 pass
375 pass
321 if not user:
376 if not user:
322 raise util.Abort(_('no username supplied (see "hg help config")'))
377 raise util.Abort(_('no username supplied (see "hg help config")'))
323 if "\n" in user:
378 if "\n" in user:
324 raise util.Abort(_("username %s contains a newline\n") % repr(user))
379 raise util.Abort(_("username %s contains a newline\n") % repr(user))
325 return user
380 return user
326
381
327 def shortuser(self, user):
382 def shortuser(self, user):
328 """Return a short representation of a user name or email address."""
383 """Return a short representation of a user name or email address."""
329 if not self.verbose:
384 if not self.verbose:
330 user = util.shortuser(user)
385 user = util.shortuser(user)
331 return user
386 return user
332
387
333 def expandpath(self, loc, default=None):
388 def expandpath(self, loc, default=None):
334 """Return repository location relative to cwd or from [paths]"""
389 """Return repository location relative to cwd or from [paths]"""
335 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
390 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
336 return loc
391 return loc
337
392
338 path = self.config('paths', loc)
393 path = self.config('paths', loc)
339 if not path and default is not None:
394 if not path and default is not None:
340 path = self.config('paths', default)
395 path = self.config('paths', default)
341 return path or loc
396 return path or loc
342
397
343 def pushbuffer(self):
398 def pushbuffer(self):
344 self._buffers.append([])
399 self._buffers.append([])
345
400
346 def popbuffer(self, labeled=False):
401 def popbuffer(self, labeled=False):
347 '''pop the last buffer and return the buffered output
402 '''pop the last buffer and return the buffered output
348
403
349 If labeled is True, any labels associated with buffered
404 If labeled is True, any labels associated with buffered
350 output will be handled. By default, this has no effect
405 output will be handled. By default, this has no effect
351 on the output returned, but extensions and GUI tools may
406 on the output returned, but extensions and GUI tools may
352 handle this argument and returned styled output. If output
407 handle this argument and returned styled output. If output
353 is being buffered so it can be captured and parsed or
408 is being buffered so it can be captured and parsed or
354 processed, labeled should not be set to True.
409 processed, labeled should not be set to True.
355 '''
410 '''
356 return "".join(self._buffers.pop())
411 return "".join(self._buffers.pop())
357
412
358 def write(self, *args, **opts):
413 def write(self, *args, **opts):
359 '''write args to output
414 '''write args to output
360
415
361 By default, this method simply writes to the buffer or stdout,
416 By default, this method simply writes to the buffer or stdout,
362 but extensions or GUI tools may override this method,
417 but extensions or GUI tools may override this method,
363 write_err(), popbuffer(), and label() to style output from
418 write_err(), popbuffer(), and label() to style output from
364 various parts of hg.
419 various parts of hg.
365
420
366 An optional keyword argument, "label", can be passed in.
421 An optional keyword argument, "label", can be passed in.
367 This should be a string containing label names separated by
422 This should be a string containing label names separated by
368 space. Label names take the form of "topic.type". For example,
423 space. Label names take the form of "topic.type". For example,
369 ui.debug() issues a label of "ui.debug".
424 ui.debug() issues a label of "ui.debug".
370
425
371 When labeling output for a specific command, a label of
426 When labeling output for a specific command, a label of
372 "cmdname.type" is recommended. For example, status issues
427 "cmdname.type" is recommended. For example, status issues
373 a label of "status.modified" for modified files.
428 a label of "status.modified" for modified files.
374 '''
429 '''
375 if self._buffers:
430 if self._buffers:
376 self._buffers[-1].extend([str(a) for a in args])
431 self._buffers[-1].extend([str(a) for a in args])
377 else:
432 else:
378 for a in args:
433 for a in args:
379 sys.stdout.write(str(a))
434 sys.stdout.write(str(a))
380
435
381 def write_err(self, *args, **opts):
436 def write_err(self, *args, **opts):
382 try:
437 try:
383 if not getattr(sys.stdout, 'closed', False):
438 if not getattr(sys.stdout, 'closed', False):
384 sys.stdout.flush()
439 sys.stdout.flush()
385 for a in args:
440 for a in args:
386 sys.stderr.write(str(a))
441 sys.stderr.write(str(a))
387 # stderr may be buffered under win32 when redirected to files,
442 # stderr may be buffered under win32 when redirected to files,
388 # including stdout.
443 # including stdout.
389 if not getattr(sys.stderr, 'closed', False):
444 if not getattr(sys.stderr, 'closed', False):
390 sys.stderr.flush()
445 sys.stderr.flush()
391 except IOError, inst:
446 except IOError, inst:
392 if inst.errno not in (errno.EPIPE, errno.EIO):
447 if inst.errno not in (errno.EPIPE, errno.EIO):
393 raise
448 raise
394
449
395 def flush(self):
450 def flush(self):
396 try: sys.stdout.flush()
451 try: sys.stdout.flush()
397 except: pass
452 except: pass
398 try: sys.stderr.flush()
453 try: sys.stderr.flush()
399 except: pass
454 except: pass
400
455
401 def interactive(self):
456 def interactive(self):
402 '''is interactive input allowed?
457 '''is interactive input allowed?
403
458
404 An interactive session is a session where input can be reasonably read
459 An interactive session is a session where input can be reasonably read
405 from `sys.stdin'. If this function returns false, any attempt to read
460 from `sys.stdin'. If this function returns false, any attempt to read
406 from stdin should fail with an error, unless a sensible default has been
461 from stdin should fail with an error, unless a sensible default has been
407 specified.
462 specified.
408
463
409 Interactiveness is triggered by the value of the `ui.interactive'
464 Interactiveness is triggered by the value of the `ui.interactive'
410 configuration variable or - if it is unset - when `sys.stdin' points
465 configuration variable or - if it is unset - when `sys.stdin' points
411 to a terminal device.
466 to a terminal device.
412
467
413 This function refers to input only; for output, see `ui.formatted()'.
468 This function refers to input only; for output, see `ui.formatted()'.
414 '''
469 '''
415 i = self.configbool("ui", "interactive", None)
470 i = self.configbool("ui", "interactive", None)
416 if i is None:
471 if i is None:
417 try:
472 try:
418 return sys.stdin.isatty()
473 return sys.stdin.isatty()
419 except AttributeError:
474 except AttributeError:
420 # some environments replace stdin without implementing isatty
475 # some environments replace stdin without implementing isatty
421 # usually those are non-interactive
476 # usually those are non-interactive
422 return False
477 return False
423
478
424 return i
479 return i
425
480
426 def termwidth(self):
481 def termwidth(self):
427 '''how wide is the terminal in columns?
482 '''how wide is the terminal in columns?
428 '''
483 '''
429 if 'COLUMNS' in os.environ:
484 if 'COLUMNS' in os.environ:
430 try:
485 try:
431 return int(os.environ['COLUMNS'])
486 return int(os.environ['COLUMNS'])
432 except ValueError:
487 except ValueError:
433 pass
488 pass
434 return util.termwidth()
489 return util.termwidth()
435
490
436 def formatted(self):
491 def formatted(self):
437 '''should formatted output be used?
492 '''should formatted output be used?
438
493
439 It is often desirable to format the output to suite the output medium.
494 It is often desirable to format the output to suite the output medium.
440 Examples of this are truncating long lines or colorizing messages.
495 Examples of this are truncating long lines or colorizing messages.
441 However, this is not often not desirable when piping output into other
496 However, this is not often not desirable when piping output into other
442 utilities, e.g. `grep'.
497 utilities, e.g. `grep'.
443
498
444 Formatted output is triggered by the value of the `ui.formatted'
499 Formatted output is triggered by the value of the `ui.formatted'
445 configuration variable or - if it is unset - when `sys.stdout' points
500 configuration variable or - if it is unset - when `sys.stdout' points
446 to a terminal device. Please note that `ui.formatted' should be
501 to a terminal device. Please note that `ui.formatted' should be
447 considered an implementation detail; it is not intended for use outside
502 considered an implementation detail; it is not intended for use outside
448 Mercurial or its extensions.
503 Mercurial or its extensions.
449
504
450 This function refers to output only; for input, see `ui.interactive()'.
505 This function refers to output only; for input, see `ui.interactive()'.
451 This function always returns false when in plain mode, see `ui.plain()'.
506 This function always returns false when in plain mode, see `ui.plain()'.
452 '''
507 '''
453 if self.plain():
508 if self.plain():
454 return False
509 return False
455
510
456 i = self.configbool("ui", "formatted", None)
511 i = self.configbool("ui", "formatted", None)
457 if i is None:
512 if i is None:
458 try:
513 try:
459 return sys.stdout.isatty()
514 return sys.stdout.isatty()
460 except AttributeError:
515 except AttributeError:
461 # some environments replace stdout without implementing isatty
516 # some environments replace stdout without implementing isatty
462 # usually those are non-interactive
517 # usually those are non-interactive
463 return False
518 return False
464
519
465 return i
520 return i
466
521
467 def _readline(self, prompt=''):
522 def _readline(self, prompt=''):
468 if sys.stdin.isatty():
523 if sys.stdin.isatty():
469 try:
524 try:
470 # magically add command line editing support, where
525 # magically add command line editing support, where
471 # available
526 # available
472 import readline
527 import readline
473 # force demandimport to really load the module
528 # force demandimport to really load the module
474 readline.read_history_file
529 readline.read_history_file
475 # windows sometimes raises something other than ImportError
530 # windows sometimes raises something other than ImportError
476 except Exception:
531 except Exception:
477 pass
532 pass
478 line = raw_input(prompt)
533 line = raw_input(prompt)
479 # When stdin is in binary mode on Windows, it can cause
534 # When stdin is in binary mode on Windows, it can cause
480 # raw_input() to emit an extra trailing carriage return
535 # raw_input() to emit an extra trailing carriage return
481 if os.linesep == '\r\n' and line and line[-1] == '\r':
536 if os.linesep == '\r\n' and line and line[-1] == '\r':
482 line = line[:-1]
537 line = line[:-1]
483 return line
538 return line
484
539
485 def prompt(self, msg, default="y"):
540 def prompt(self, msg, default="y"):
486 """Prompt user with msg, read response.
541 """Prompt user with msg, read response.
487 If ui is not interactive, the default is returned.
542 If ui is not interactive, the default is returned.
488 """
543 """
489 if not self.interactive():
544 if not self.interactive():
490 self.write(msg, ' ', default, "\n")
545 self.write(msg, ' ', default, "\n")
491 return default
546 return default
492 try:
547 try:
493 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
548 r = self._readline(self.label(msg, 'ui.prompt') + ' ')
494 if not r:
549 if not r:
495 return default
550 return default
496 return r
551 return r
497 except EOFError:
552 except EOFError:
498 raise util.Abort(_('response expected'))
553 raise util.Abort(_('response expected'))
499
554
500 def promptchoice(self, msg, choices, default=0):
555 def promptchoice(self, msg, choices, default=0):
501 """Prompt user with msg, read response, and ensure it matches
556 """Prompt user with msg, read response, and ensure it matches
502 one of the provided choices. The index of the choice is returned.
557 one of the provided choices. The index of the choice is returned.
503 choices is a sequence of acceptable responses with the format:
558 choices is a sequence of acceptable responses with the format:
504 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
559 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
505 If ui is not interactive, the default is returned.
560 If ui is not interactive, the default is returned.
506 """
561 """
507 resps = [s[s.index('&')+1].lower() for s in choices]
562 resps = [s[s.index('&')+1].lower() for s in choices]
508 while True:
563 while True:
509 r = self.prompt(msg, resps[default])
564 r = self.prompt(msg, resps[default])
510 if r.lower() in resps:
565 if r.lower() in resps:
511 return resps.index(r.lower())
566 return resps.index(r.lower())
512 self.write(_("unrecognized response\n"))
567 self.write(_("unrecognized response\n"))
513
568
514 def getpass(self, prompt=None, default=None):
569 def getpass(self, prompt=None, default=None):
515 if not self.interactive():
570 if not self.interactive():
516 return default
571 return default
517 try:
572 try:
518 return getpass.getpass(prompt or _('password: '))
573 return getpass.getpass(prompt or _('password: '))
519 except EOFError:
574 except EOFError:
520 raise util.Abort(_('response expected'))
575 raise util.Abort(_('response expected'))
521 def status(self, *msg, **opts):
576 def status(self, *msg, **opts):
522 '''write status message to output (if ui.quiet is False)
577 '''write status message to output (if ui.quiet is False)
523
578
524 This adds an output label of "ui.status".
579 This adds an output label of "ui.status".
525 '''
580 '''
526 if not self.quiet:
581 if not self.quiet:
527 opts['label'] = opts.get('label', '') + ' ui.status'
582 opts['label'] = opts.get('label', '') + ' ui.status'
528 self.write(*msg, **opts)
583 self.write(*msg, **opts)
529 def warn(self, *msg, **opts):
584 def warn(self, *msg, **opts):
530 '''write warning message to output (stderr)
585 '''write warning message to output (stderr)
531
586
532 This adds an output label of "ui.warning".
587 This adds an output label of "ui.warning".
533 '''
588 '''
534 opts['label'] = opts.get('label', '') + ' ui.warning'
589 opts['label'] = opts.get('label', '') + ' ui.warning'
535 self.write_err(*msg, **opts)
590 self.write_err(*msg, **opts)
536 def note(self, *msg, **opts):
591 def note(self, *msg, **opts):
537 '''write note to output (if ui.verbose is True)
592 '''write note to output (if ui.verbose is True)
538
593
539 This adds an output label of "ui.note".
594 This adds an output label of "ui.note".
540 '''
595 '''
541 if self.verbose:
596 if self.verbose:
542 opts['label'] = opts.get('label', '') + ' ui.note'
597 opts['label'] = opts.get('label', '') + ' ui.note'
543 self.write(*msg, **opts)
598 self.write(*msg, **opts)
544 def debug(self, *msg, **opts):
599 def debug(self, *msg, **opts):
545 '''write debug message to output (if ui.debugflag is True)
600 '''write debug message to output (if ui.debugflag is True)
546
601
547 This adds an output label of "ui.debug".
602 This adds an output label of "ui.debug".
548 '''
603 '''
549 if self.debugflag:
604 if self.debugflag:
550 opts['label'] = opts.get('label', '') + ' ui.debug'
605 opts['label'] = opts.get('label', '') + ' ui.debug'
551 self.write(*msg, **opts)
606 self.write(*msg, **opts)
552 def edit(self, text, user):
607 def edit(self, text, user):
553 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
608 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
554 text=True)
609 text=True)
555 try:
610 try:
556 f = os.fdopen(fd, "w")
611 f = os.fdopen(fd, "w")
557 f.write(text)
612 f.write(text)
558 f.close()
613 f.close()
559
614
560 editor = self.geteditor()
615 editor = self.geteditor()
561
616
562 util.system("%s \"%s\"" % (editor, name),
617 util.system("%s \"%s\"" % (editor, name),
563 environ={'HGUSER': user},
618 environ={'HGUSER': user},
564 onerr=util.Abort, errprefix=_("edit failed"))
619 onerr=util.Abort, errprefix=_("edit failed"))
565
620
566 f = open(name)
621 f = open(name)
567 t = f.read()
622 t = f.read()
568 f.close()
623 f.close()
569 finally:
624 finally:
570 os.unlink(name)
625 os.unlink(name)
571
626
572 return t
627 return t
573
628
574 def traceback(self, exc=None):
629 def traceback(self, exc=None):
575 '''print exception traceback if traceback printing enabled.
630 '''print exception traceback if traceback printing enabled.
576 only to call in exception handler. returns true if traceback
631 only to call in exception handler. returns true if traceback
577 printed.'''
632 printed.'''
578 if self.tracebackflag:
633 if self.tracebackflag:
579 if exc:
634 if exc:
580 traceback.print_exception(exc[0], exc[1], exc[2])
635 traceback.print_exception(exc[0], exc[1], exc[2])
581 else:
636 else:
582 traceback.print_exc()
637 traceback.print_exc()
583 return self.tracebackflag
638 return self.tracebackflag
584
639
585 def geteditor(self):
640 def geteditor(self):
586 '''return editor to use'''
641 '''return editor to use'''
587 return (os.environ.get("HGEDITOR") or
642 return (os.environ.get("HGEDITOR") or
588 self.config("ui", "editor") or
643 self.config("ui", "editor") or
589 os.environ.get("VISUAL") or
644 os.environ.get("VISUAL") or
590 os.environ.get("EDITOR", "vi"))
645 os.environ.get("EDITOR", "vi"))
591
646
592 def progress(self, topic, pos, item="", unit="", total=None):
647 def progress(self, topic, pos, item="", unit="", total=None):
593 '''show a progress message
648 '''show a progress message
594
649
595 With stock hg, this is simply a debug message that is hidden
650 With stock hg, this is simply a debug message that is hidden
596 by default, but with extensions or GUI tools it may be
651 by default, but with extensions or GUI tools it may be
597 visible. 'topic' is the current operation, 'item' is a
652 visible. 'topic' is the current operation, 'item' is a
598 non-numeric marker of the current position (ie the currently
653 non-numeric marker of the current position (ie the currently
599 in-process file), 'pos' is the current numeric position (ie
654 in-process file), 'pos' is the current numeric position (ie
600 revision, bytes, etc.), unit is a corresponding unit label,
655 revision, bytes, etc.), unit is a corresponding unit label,
601 and total is the highest expected pos.
656 and total is the highest expected pos.
602
657
603 Multiple nested topics may be active at a time.
658 Multiple nested topics may be active at a time.
604
659
605 All topics should be marked closed by setting pos to None at
660 All topics should be marked closed by setting pos to None at
606 termination.
661 termination.
607 '''
662 '''
608
663
609 if pos is None or not self.debugflag:
664 if pos is None or not self.debugflag:
610 return
665 return
611
666
612 if unit:
667 if unit:
613 unit = ' ' + unit
668 unit = ' ' + unit
614 if item:
669 if item:
615 item = ' ' + item
670 item = ' ' + item
616
671
617 if total:
672 if total:
618 pct = 100.0 * pos / total
673 pct = 100.0 * pos / total
619 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
674 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
620 % (topic, item, pos, total, unit, pct))
675 % (topic, item, pos, total, unit, pct))
621 else:
676 else:
622 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
677 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
623
678
624 def log(self, service, message):
679 def log(self, service, message):
625 '''hook for logging facility extensions
680 '''hook for logging facility extensions
626
681
627 service should be a readily-identifiable subsystem, which will
682 service should be a readily-identifiable subsystem, which will
628 allow filtering.
683 allow filtering.
629 message should be a newline-terminated string to log.
684 message should be a newline-terminated string to log.
630 '''
685 '''
631 pass
686 pass
632
687
633 def label(self, msg, label):
688 def label(self, msg, label):
634 '''style msg based on supplied label
689 '''style msg based on supplied label
635
690
636 Like ui.write(), this just returns msg unchanged, but extensions
691 Like ui.write(), this just returns msg unchanged, but extensions
637 and GUI tools can override it to allow styling output without
692 and GUI tools can override it to allow styling output without
638 writing it.
693 writing it.
639
694
640 ui.write(s, 'label') is equivalent to
695 ui.write(s, 'label') is equivalent to
641 ui.write(ui.label(s, 'label')).
696 ui.write(ui.label(s, 'label')).
642 '''
697 '''
643 return msg
698 return msg
@@ -1,32 +1,35 b''
1 # this is hack to make sure no escape characters are inserted into the output
1 # this is hack to make sure no escape characters are inserted into the output
2 import os
2 import os
3 if 'TERM' in os.environ:
3 if 'TERM' in os.environ:
4 del os.environ['TERM']
4 del os.environ['TERM']
5 import doctest
5 import doctest
6
6
7 import mercurial.changelog
7 import mercurial.changelog
8 doctest.testmod(mercurial.changelog)
8 doctest.testmod(mercurial.changelog)
9
9
10 import mercurial.dagparser
10 import mercurial.dagparser
11 doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
11 doctest.testmod(mercurial.dagparser, optionflags=doctest.NORMALIZE_WHITESPACE)
12
12
13 import mercurial.match
13 import mercurial.match
14 doctest.testmod(mercurial.match)
14 doctest.testmod(mercurial.match)
15
15
16 import mercurial.store
16 import mercurial.store
17 doctest.testmod(mercurial.store)
17 doctest.testmod(mercurial.store)
18
18
19 import mercurial.ui
20 doctest.testmod(mercurial.ui)
21
19 import mercurial.url
22 import mercurial.url
20 doctest.testmod(mercurial.url)
23 doctest.testmod(mercurial.url)
21
24
22 import mercurial.util
25 import mercurial.util
23 doctest.testmod(mercurial.util)
26 doctest.testmod(mercurial.util)
24
27
25 import mercurial.encoding
28 import mercurial.encoding
26 doctest.testmod(mercurial.encoding)
29 doctest.testmod(mercurial.encoding)
27
30
28 import mercurial.hgweb.hgwebdir_mod
31 import mercurial.hgweb.hgwebdir_mod
29 doctest.testmod(mercurial.hgweb.hgwebdir_mod)
32 doctest.testmod(mercurial.hgweb.hgwebdir_mod)
30
33
31 import hgext.convert.cvsps
34 import hgext.convert.cvsps
32 doctest.testmod(hgext.convert.cvsps)
35 doctest.testmod(hgext.convert.cvsps)
@@ -1,81 +1,98 b''
1 from mercurial import ui, dispatch, error
1 from mercurial import ui, dispatch, error
2
2
3 testui = ui.ui()
3 testui = ui.ui()
4 parsed = dispatch._parseconfig(testui, [
4 parsed = dispatch._parseconfig(testui, [
5 'values.string=string value',
5 'values.string=string value',
6 'values.bool1=true',
6 'values.bool1=true',
7 'values.bool2=false',
7 'values.bool2=false',
8 'values.boolinvalid=foo',
9 'values.int1=42',
10 'values.int2=-42',
11 'values.intinvalid=foo',
8 'lists.list1=foo',
12 'lists.list1=foo',
9 'lists.list2=foo bar baz',
13 'lists.list2=foo bar baz',
10 'lists.list3=alice, bob',
14 'lists.list3=alice, bob',
11 'lists.list4=foo bar baz alice, bob',
15 'lists.list4=foo bar baz alice, bob',
12 'lists.list5=abc d"ef"g "hij def"',
16 'lists.list5=abc d"ef"g "hij def"',
13 'lists.list6="hello world", "how are you?"',
17 'lists.list6="hello world", "how are you?"',
14 'lists.list7=Do"Not"Separate',
18 'lists.list7=Do"Not"Separate',
15 'lists.list8="Do"Separate',
19 'lists.list8="Do"Separate',
16 'lists.list9="Do\\"NotSeparate"',
20 'lists.list9="Do\\"NotSeparate"',
17 'lists.list10=string "with extraneous" quotation mark"',
21 'lists.list10=string "with extraneous" quotation mark"',
18 'lists.list11=x, y',
22 'lists.list11=x, y',
19 'lists.list12="x", "y"',
23 'lists.list12="x", "y"',
20 'lists.list13=""" key = "x", "y" """',
24 'lists.list13=""" key = "x", "y" """',
21 'lists.list14=,,,, ',
25 'lists.list14=,,,, ',
22 'lists.list15=" just with starting quotation',
26 'lists.list15=" just with starting quotation',
23 'lists.list16="longer quotation" with "no ending quotation',
27 'lists.list16="longer quotation" with "no ending quotation',
24 'lists.list17=this is \\" "not a quotation mark"',
28 'lists.list17=this is \\" "not a quotation mark"',
25 'lists.list18=\n \n\nding\ndong',
29 'lists.list18=\n \n\nding\ndong',
26 ])
30 ])
27
31
28 print repr(testui.configitems('values'))
32 print repr(testui.configitems('values'))
29 print repr(testui.configitems('lists'))
33 print repr(testui.configitems('lists'))
30 print "---"
34 print "---"
31 print repr(testui.config('values', 'string'))
35 print repr(testui.config('values', 'string'))
32 print repr(testui.config('values', 'bool1'))
36 print repr(testui.config('values', 'bool1'))
33 print repr(testui.config('values', 'bool2'))
37 print repr(testui.config('values', 'bool2'))
34 print repr(testui.config('values', 'unknown'))
38 print repr(testui.config('values', 'unknown'))
35 print "---"
39 print "---"
36 try:
40 try:
37 print repr(testui.configbool('values', 'string'))
41 print repr(testui.configbool('values', 'string'))
38 except error.ConfigError, inst:
42 except error.ConfigError, inst:
39 print inst
43 print inst
40 print repr(testui.configbool('values', 'bool1'))
44 print repr(testui.configbool('values', 'bool1'))
41 print repr(testui.configbool('values', 'bool2'))
45 print repr(testui.configbool('values', 'bool2'))
42 print repr(testui.configbool('values', 'bool2', True))
46 print repr(testui.configbool('values', 'bool2', True))
43 print repr(testui.configbool('values', 'unknown'))
47 print repr(testui.configbool('values', 'unknown'))
44 print repr(testui.configbool('values', 'unknown', True))
48 print repr(testui.configbool('values', 'unknown', True))
45 print "---"
49 print "---"
50 print repr(testui.configint('values', 'int1'))
51 print repr(testui.configint('values', 'int2'))
52 print "---"
46 print repr(testui.configlist('lists', 'list1'))
53 print repr(testui.configlist('lists', 'list1'))
47 print repr(testui.configlist('lists', 'list2'))
54 print repr(testui.configlist('lists', 'list2'))
48 print repr(testui.configlist('lists', 'list3'))
55 print repr(testui.configlist('lists', 'list3'))
49 print repr(testui.configlist('lists', 'list4'))
56 print repr(testui.configlist('lists', 'list4'))
50 print repr(testui.configlist('lists', 'list4', ['foo']))
57 print repr(testui.configlist('lists', 'list4', ['foo']))
51 print repr(testui.configlist('lists', 'list5'))
58 print repr(testui.configlist('lists', 'list5'))
52 print repr(testui.configlist('lists', 'list6'))
59 print repr(testui.configlist('lists', 'list6'))
53 print repr(testui.configlist('lists', 'list7'))
60 print repr(testui.configlist('lists', 'list7'))
54 print repr(testui.configlist('lists', 'list8'))
61 print repr(testui.configlist('lists', 'list8'))
55 print repr(testui.configlist('lists', 'list9'))
62 print repr(testui.configlist('lists', 'list9'))
56 print repr(testui.configlist('lists', 'list10'))
63 print repr(testui.configlist('lists', 'list10'))
57 print repr(testui.configlist('lists', 'list11'))
64 print repr(testui.configlist('lists', 'list11'))
58 print repr(testui.configlist('lists', 'list12'))
65 print repr(testui.configlist('lists', 'list12'))
59 print repr(testui.configlist('lists', 'list13'))
66 print repr(testui.configlist('lists', 'list13'))
60 print repr(testui.configlist('lists', 'list14'))
67 print repr(testui.configlist('lists', 'list14'))
61 print repr(testui.configlist('lists', 'list15'))
68 print repr(testui.configlist('lists', 'list15'))
62 print repr(testui.configlist('lists', 'list16'))
69 print repr(testui.configlist('lists', 'list16'))
63 print repr(testui.configlist('lists', 'list17'))
70 print repr(testui.configlist('lists', 'list17'))
64 print repr(testui.configlist('lists', 'list18'))
71 print repr(testui.configlist('lists', 'list18'))
65 print repr(testui.configlist('lists', 'unknown'))
72 print repr(testui.configlist('lists', 'unknown'))
66 print repr(testui.configlist('lists', 'unknown', ''))
73 print repr(testui.configlist('lists', 'unknown', ''))
67 print repr(testui.configlist('lists', 'unknown', 'foo'))
74 print repr(testui.configlist('lists', 'unknown', 'foo'))
68 print repr(testui.configlist('lists', 'unknown', ['foo']))
75 print repr(testui.configlist('lists', 'unknown', ['foo']))
69 print repr(testui.configlist('lists', 'unknown', 'foo bar'))
76 print repr(testui.configlist('lists', 'unknown', 'foo bar'))
70 print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
77 print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
71 print repr(testui.configlist('lists', 'unknown', ['foo bar']))
78 print repr(testui.configlist('lists', 'unknown', ['foo bar']))
72 print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
79 print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
73
80
74 print repr(testui.config('values', 'String'))
81 print repr(testui.config('values', 'String'))
75
82
76 def function():
83 def function():
77 pass
84 pass
78
85
79 # values that aren't strings should work
86 # values that aren't strings should work
80 testui.setconfig('hook', 'commit', function)
87 testui.setconfig('hook', 'commit', function)
81 print function == testui.config('hook', 'commit')
88 print function == testui.config('hook', 'commit')
89
90 # invalid values
91 try:
92 testui.configbool('values', 'boolinvalid')
93 except error.ConfigError:
94 print 'boolinvalid'
95 try:
96 testui.configint('values', 'intinvalid')
97 except error.ConfigError:
98 print 'intinvalid'
@@ -1,44 +1,49 b''
1 [('string', 'string value'), ('bool1', 'true'), ('bool2', 'false')]
1 [('string', 'string value'), ('bool1', 'true'), ('bool2', 'false'), ('boolinvalid', 'foo'), ('int1', '42'), ('int2', '-42'), ('intinvalid', 'foo')]
2 [('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob'), ('list5', 'abc d"ef"g "hij def"'), ('list6', '"hello world", "how are you?"'), ('list7', 'Do"Not"Separate'), ('list8', '"Do"Separate'), ('list9', '"Do\\"NotSeparate"'), ('list10', 'string "with extraneous" quotation mark"'), ('list11', 'x, y'), ('list12', '"x", "y"'), ('list13', '""" key = "x", "y" """'), ('list14', ',,,, '), ('list15', '" just with starting quotation'), ('list16', '"longer quotation" with "no ending quotation'), ('list17', 'this is \\" "not a quotation mark"'), ('list18', '\n \n\nding\ndong')]
2 [('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob'), ('list5', 'abc d"ef"g "hij def"'), ('list6', '"hello world", "how are you?"'), ('list7', 'Do"Not"Separate'), ('list8', '"Do"Separate'), ('list9', '"Do\\"NotSeparate"'), ('list10', 'string "with extraneous" quotation mark"'), ('list11', 'x, y'), ('list12', '"x", "y"'), ('list13', '""" key = "x", "y" """'), ('list14', ',,,, '), ('list15', '" just with starting quotation'), ('list16', '"longer quotation" with "no ending quotation'), ('list17', 'this is \\" "not a quotation mark"'), ('list18', '\n \n\nding\ndong')]
3 ---
3 ---
4 'string value'
4 'string value'
5 'true'
5 'true'
6 'false'
6 'false'
7 None
7 None
8 ---
8 ---
9 values.string not a boolean ('string value')
9 values.string is not a boolean ('string value')
10 True
10 True
11 False
11 False
12 False
12 False
13 False
13 False
14 True
14 True
15 ---
15 ---
16 42
17 -42
18 ---
16 ['foo']
19 ['foo']
17 ['foo', 'bar', 'baz']
20 ['foo', 'bar', 'baz']
18 ['alice', 'bob']
21 ['alice', 'bob']
19 ['foo', 'bar', 'baz', 'alice', 'bob']
22 ['foo', 'bar', 'baz', 'alice', 'bob']
20 ['foo', 'bar', 'baz', 'alice', 'bob']
23 ['foo', 'bar', 'baz', 'alice', 'bob']
21 ['abc', 'd"ef"g', 'hij def']
24 ['abc', 'd"ef"g', 'hij def']
22 ['hello world', 'how are you?']
25 ['hello world', 'how are you?']
23 ['Do"Not"Separate']
26 ['Do"Not"Separate']
24 ['Do', 'Separate']
27 ['Do', 'Separate']
25 ['Do"NotSeparate']
28 ['Do"NotSeparate']
26 ['string', 'with extraneous', 'quotation', 'mark"']
29 ['string', 'with extraneous', 'quotation', 'mark"']
27 ['x', 'y']
30 ['x', 'y']
28 ['x', 'y']
31 ['x', 'y']
29 ['', ' key = ', 'x"', 'y', '', '"']
32 ['', ' key = ', 'x"', 'y', '', '"']
30 []
33 []
31 ['"', 'just', 'with', 'starting', 'quotation']
34 ['"', 'just', 'with', 'starting', 'quotation']
32 ['longer quotation', 'with', '"no', 'ending', 'quotation']
35 ['longer quotation', 'with', '"no', 'ending', 'quotation']
33 ['this', 'is', '"', 'not a quotation mark']
36 ['this', 'is', '"', 'not a quotation mark']
34 ['ding', 'dong']
37 ['ding', 'dong']
35 []
38 []
36 []
39 []
37 ['foo']
40 ['foo']
38 ['foo']
41 ['foo']
39 ['foo', 'bar']
42 ['foo', 'bar']
40 ['foo', 'bar']
43 ['foo', 'bar']
41 ['foo bar']
44 ['foo bar']
42 ['foo', 'bar']
45 ['foo', 'bar']
43 None
46 None
44 True
47 True
48 boolinvalid
49 intinvalid
General Comments 0
You need to be logged in to leave comments. Login now