##// END OF EJS Templates
Allow using default values with ui.configlist, too, and add a test for this.
Thomas Arendsen Hein -
r2502:18cf95ad default
parent child Browse files
Show More
@@ -0,0 +1,47 b''
1 #!/usr/bin/env python
2
3 from mercurial import ui
4
5 testui = ui.ui()
6 testui.updateopts(config=[
7 'values.string=string value',
8 'values.bool1=true',
9 'values.bool2=false',
10 'lists.list1=foo',
11 'lists.list2=foo bar baz',
12 'lists.list3=alice, bob',
13 'lists.list4=foo bar baz alice, bob',
14 ])
15
16 print repr(testui.configitems('values'))
17 print repr(testui.configitems('lists'))
18 print "---"
19 print repr(testui.config('values', 'string'))
20 print repr(testui.config('values', 'bool1'))
21 print repr(testui.config('values', 'bool2'))
22 print repr(testui.config('values', 'unknown'))
23 print "---"
24 try:
25 print repr(testui.configbool('values', 'string'))
26 except ValueError, why:
27 print why
28 print repr(testui.configbool('values', 'bool1'))
29 print repr(testui.configbool('values', 'bool2'))
30 print repr(testui.configbool('values', 'bool2', True))
31 print repr(testui.configbool('values', 'unknown'))
32 print repr(testui.configbool('values', 'unknown', True))
33 print "---"
34 print repr(testui.configlist('lists', 'list1'))
35 print repr(testui.configlist('lists', 'list2'))
36 print repr(testui.configlist('lists', 'list3'))
37 print repr(testui.configlist('lists', 'list4'))
38 print repr(testui.configlist('lists', 'list4', ['foo']))
39 print repr(testui.configlist('lists', 'unknown'))
40 print repr(testui.configlist('lists', 'unknown', ''))
41 print repr(testui.configlist('lists', 'unknown', 'foo'))
42 print repr(testui.configlist('lists', 'unknown', ['foo']))
43 print repr(testui.configlist('lists', 'unknown', 'foo bar'))
44 print repr(testui.configlist('lists', 'unknown', 'foo, bar'))
45 print repr(testui.configlist('lists', 'unknown', ['foo bar']))
46 print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
47 print "---"
@@ -0,0 +1,29 b''
1 [('bool1', 'true'), ('bool2', 'false'), ('string', 'string value')]
2 [('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')]
3 ---
4 'string value'
5 'true'
6 'false'
7 None
8 ---
9 Not a boolean: string value
10 True
11 False
12 False
13 False
14 True
15 ---
16 ['foo']
17 ['foo', 'bar', 'baz']
18 ['alice', 'bob']
19 ['foo', 'bar', 'baz', 'alice', 'bob']
20 ['foo', 'bar', 'baz', 'alice', 'bob']
21 []
22 []
23 ['foo']
24 ['foo']
25 ['foo', 'bar']
26 ['foo', 'bar']
27 ['foo bar']
28 ['foo', 'bar']
29 ---
@@ -1,355 +1,355 b''
1 1 # ui.py - user interface bits for mercurial
2 2 #
3 3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from i18n import gettext as _
9 9 from demandload import *
10 10 demandload(globals(), "errno getpass os re smtplib socket sys tempfile")
11 11 demandload(globals(), "ConfigParser templater traceback util")
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 15 interactive=True, traceback=False, parentui=None):
16 16 self.overlay = {}
17 17 if parentui is None:
18 18 # this is the parent of all ui children
19 19 self.parentui = None
20 20 self.cdata = ConfigParser.SafeConfigParser()
21 21 self.readconfig(util.rcpath())
22 22
23 23 self.quiet = self.configbool("ui", "quiet")
24 24 self.verbose = self.configbool("ui", "verbose")
25 25 self.debugflag = self.configbool("ui", "debug")
26 26 self.interactive = self.configbool("ui", "interactive", True)
27 27 self.traceback = traceback
28 28
29 29 self.updateopts(verbose, debug, quiet, interactive)
30 30 self.diffcache = None
31 31 self.header = []
32 32 self.prev_header = []
33 33 self.revlogopts = self.configrevlog()
34 34 else:
35 35 # parentui may point to an ui object which is already a child
36 36 self.parentui = parentui.parentui or parentui
37 37 parent_cdata = self.parentui.cdata
38 38 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
39 39 # make interpolation work
40 40 for section in parent_cdata.sections():
41 41 self.cdata.add_section(section)
42 42 for name, value in parent_cdata.items(section, raw=True):
43 43 self.cdata.set(section, name, value)
44 44
45 45 def __getattr__(self, key):
46 46 return getattr(self.parentui, key)
47 47
48 48 def updateopts(self, verbose=False, debug=False, quiet=False,
49 49 interactive=True, traceback=False, config=[]):
50 50 self.quiet = (self.quiet or quiet) and not verbose and not debug
51 51 self.verbose = (self.verbose or verbose) or debug
52 52 self.debugflag = (self.debugflag or debug)
53 53 self.interactive = (self.interactive and interactive)
54 54 self.traceback = self.traceback or traceback
55 55 for cfg in config:
56 56 try:
57 57 name, value = cfg.split('=', 1)
58 58 section, name = name.split('.', 1)
59 59 if not self.cdata.has_section(section):
60 60 self.cdata.add_section(section)
61 61 if not section or not name:
62 62 raise IndexError
63 63 self.cdata.set(section, name, value)
64 64 except (IndexError, ValueError):
65 65 raise util.Abort(_('malformed --config option: %s') % cfg)
66 66
67 67 def readconfig(self, fn, root=None):
68 68 if isinstance(fn, basestring):
69 69 fn = [fn]
70 70 for f in fn:
71 71 try:
72 72 self.cdata.read(f)
73 73 except ConfigParser.ParsingError, inst:
74 74 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
75 75 # translate paths relative to root (or home) into absolute paths
76 76 if root is None:
77 77 root = os.path.expanduser('~')
78 78 for name, path in self.configitems("paths"):
79 79 if path and path.find("://") == -1 and not os.path.isabs(path):
80 80 self.cdata.set("paths", name, os.path.join(root, path))
81 81
82 82 def setconfig(self, section, name, val):
83 83 self.overlay[(section, name)] = val
84 84
85 85 def config(self, section, name, default=None):
86 86 if self.overlay.has_key((section, name)):
87 87 return self.overlay[(section, name)]
88 88 if self.cdata.has_option(section, name):
89 89 try:
90 90 return self.cdata.get(section, name)
91 91 except ConfigParser.InterpolationError, inst:
92 92 raise util.Abort(_("Error in configuration:\n%s") % inst)
93 93 if self.parentui is None:
94 94 return default
95 95 else:
96 96 return self.parentui.config(section, name, default)
97 97
98 98 def configlist(self, section, name, default=None):
99 99 """Return a list of comma/space separated strings"""
100 100 result = self.config(section, name)
101 101 if result is None:
102 return []
103 else:
104 return result.replace(",", " ").split()
105
102 result = default or []
103 if isinstance(result, basestring):
104 result = result.replace(",", " ").split()
105 return result
106 106
107 107 def configbool(self, section, name, default=False):
108 108 if self.overlay.has_key((section, name)):
109 109 return self.overlay[(section, name)]
110 110 if self.cdata.has_option(section, name):
111 111 try:
112 112 return self.cdata.getboolean(section, name)
113 113 except ConfigParser.InterpolationError, inst:
114 114 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 115 if self.parentui is None:
116 116 return default
117 117 else:
118 118 return self.parentui.configbool(section, name, default)
119 119
120 120 def has_config(self, section):
121 121 '''tell whether section exists in config.'''
122 122 return self.cdata.has_section(section)
123 123
124 124 def configitems(self, section):
125 125 items = {}
126 126 if self.parentui is not None:
127 127 items = dict(self.parentui.configitems(section))
128 128 if self.cdata.has_section(section):
129 129 try:
130 130 items.update(dict(self.cdata.items(section)))
131 131 except ConfigParser.InterpolationError, inst:
132 132 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 133 x = items.items()
134 134 x.sort()
135 135 return x
136 136
137 137 def walkconfig(self, seen=None):
138 138 if seen is None:
139 139 seen = {}
140 140 for (section, name), value in self.overlay.iteritems():
141 141 yield section, name, value
142 142 seen[section, name] = 1
143 143 for section in self.cdata.sections():
144 144 for name, value in self.cdata.items(section):
145 145 if (section, name) in seen: continue
146 146 yield section, name, value.replace('\n', '\\n')
147 147 seen[section, name] = 1
148 148 if self.parentui is not None:
149 149 for parent in self.parentui.walkconfig(seen):
150 150 yield parent
151 151
152 152 def extensions(self):
153 153 result = self.configitems("extensions")
154 154 for i, (key, value) in enumerate(result):
155 155 if value:
156 156 result[i] = (key, os.path.expanduser(value))
157 157 return result
158 158
159 159 def hgignorefiles(self):
160 160 result = []
161 161 for key, value in self.configitems("ui"):
162 162 if key == 'ignore' or key.startswith('ignore.'):
163 163 result.append(os.path.expanduser(value))
164 164 return result
165 165
166 166 def configrevlog(self):
167 167 result = {}
168 168 for key, value in self.configitems("revlog"):
169 169 result[key.lower()] = value
170 170 return result
171 171
172 172 def diffopts(self):
173 173 if self.diffcache:
174 174 return self.diffcache
175 175 result = {'showfunc': True, 'ignorews': False}
176 176 for key, value in self.configitems("diff"):
177 177 if value:
178 178 result[key.lower()] = (value.lower() == 'true')
179 179 self.diffcache = result
180 180 return result
181 181
182 182 def username(self):
183 183 """Return default username to be used in commits.
184 184
185 185 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 186 and stop searching if one of these is set.
187 187 Abort if found username is an empty string to force specifying
188 188 the commit user elsewhere, e.g. with line option or repo hgrc.
189 189 If not found, use ($LOGNAME or $USER or $LNAME or
190 190 $USERNAME) +"@full.hostname".
191 191 """
192 192 user = os.environ.get("HGUSER")
193 193 if user is None:
194 194 user = self.config("ui", "username")
195 195 if user is None:
196 196 user = os.environ.get("EMAIL")
197 197 if user is None:
198 198 try:
199 199 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
200 200 except KeyError:
201 201 raise util.Abort(_("Please specify a username."))
202 202 return user
203 203
204 204 def shortuser(self, user):
205 205 """Return a short representation of a user name or email address."""
206 206 if not self.verbose: user = util.shortuser(user)
207 207 return user
208 208
209 209 def expandpath(self, loc, default=None):
210 210 """Return repository location relative to cwd or from [paths]"""
211 211 if loc.find("://") != -1 or os.path.exists(loc):
212 212 return loc
213 213
214 214 path = self.config("paths", loc)
215 215 if not path and default is not None:
216 216 path = self.config("paths", default)
217 217 return path or loc
218 218
219 219 def write(self, *args):
220 220 if self.header:
221 221 if self.header != self.prev_header:
222 222 self.prev_header = self.header
223 223 self.write(*self.header)
224 224 self.header = []
225 225 for a in args:
226 226 sys.stdout.write(str(a))
227 227
228 228 def write_header(self, *args):
229 229 for a in args:
230 230 self.header.append(str(a))
231 231
232 232 def write_err(self, *args):
233 233 try:
234 234 if not sys.stdout.closed: sys.stdout.flush()
235 235 for a in args:
236 236 sys.stderr.write(str(a))
237 237 except IOError, inst:
238 238 if inst.errno != errno.EPIPE:
239 239 raise
240 240
241 241 def flush(self):
242 242 try: sys.stdout.flush()
243 243 except: pass
244 244 try: sys.stderr.flush()
245 245 except: pass
246 246
247 247 def readline(self):
248 248 return sys.stdin.readline()[:-1]
249 249 def prompt(self, msg, pat=None, default="y"):
250 250 if not self.interactive: return default
251 251 while 1:
252 252 self.write(msg, " ")
253 253 r = self.readline()
254 254 if not pat or re.match(pat, r):
255 255 return r
256 256 else:
257 257 self.write(_("unrecognized response\n"))
258 258 def getpass(self, prompt=None, default=None):
259 259 if not self.interactive: return default
260 260 return getpass.getpass(prompt or _('password: '))
261 261 def status(self, *msg):
262 262 if not self.quiet: self.write(*msg)
263 263 def warn(self, *msg):
264 264 self.write_err(*msg)
265 265 def note(self, *msg):
266 266 if self.verbose: self.write(*msg)
267 267 def debug(self, *msg):
268 268 if self.debugflag: self.write(*msg)
269 269 def edit(self, text, user):
270 270 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
271 271 text=True)
272 272 try:
273 273 f = os.fdopen(fd, "w")
274 274 f.write(text)
275 275 f.close()
276 276
277 277 editor = (os.environ.get("HGEDITOR") or
278 278 self.config("ui", "editor") or
279 279 os.environ.get("EDITOR", "vi"))
280 280
281 281 util.system("%s \"%s\"" % (editor, name),
282 282 environ={'HGUSER': user},
283 283 onerr=util.Abort, errprefix=_("edit failed"))
284 284
285 285 f = open(name)
286 286 t = f.read()
287 287 f.close()
288 288 t = re.sub("(?m)^HG:.*\n", "", t)
289 289 finally:
290 290 os.unlink(name)
291 291
292 292 return t
293 293
294 294 def sendmail(self):
295 295 '''send mail message. object returned has one method, sendmail.
296 296 call as sendmail(sender, list-of-recipients, msg).'''
297 297
298 298 def smtp():
299 299 '''send mail using smtp.'''
300 300
301 301 s = smtplib.SMTP()
302 302 mailhost = self.config('smtp', 'host')
303 303 if not mailhost:
304 304 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
305 305 mailport = int(self.config('smtp', 'port', 25))
306 306 self.note(_('sending mail: smtp host %s, port %s\n') %
307 307 (mailhost, mailport))
308 308 s.connect(host=mailhost, port=mailport)
309 309 if self.configbool('smtp', 'tls'):
310 310 self.note(_('(using tls)\n'))
311 311 s.ehlo()
312 312 s.starttls()
313 313 s.ehlo()
314 314 username = self.config('smtp', 'username')
315 315 password = self.config('smtp', 'password')
316 316 if username and password:
317 317 self.note(_('(authenticating to mail server as %s)\n') %
318 318 (username))
319 319 s.login(username, password)
320 320 return s
321 321
322 322 class sendmail(object):
323 323 '''send mail using sendmail.'''
324 324
325 325 def __init__(self, ui, program):
326 326 self.ui = ui
327 327 self.program = program
328 328
329 329 def sendmail(self, sender, recipients, msg):
330 330 cmdline = '%s -f %s %s' % (
331 331 self.program, templater.email(sender),
332 332 ' '.join(map(templater.email, recipients)))
333 333 self.ui.note(_('sending mail: %s\n') % cmdline)
334 334 fp = os.popen(cmdline, 'w')
335 335 fp.write(msg)
336 336 ret = fp.close()
337 337 if ret:
338 338 raise util.Abort('%s %s' % (
339 339 os.path.basename(self.program.split(None, 1)[0]),
340 340 util.explain_exit(ret)[0]))
341 341
342 342 method = self.config('email', 'method', 'smtp')
343 343 if method == 'smtp':
344 344 mail = smtp()
345 345 else:
346 346 mail = sendmail(self, method)
347 347 return mail
348 348
349 349 def print_exc(self):
350 350 '''print exception traceback if traceback printing enabled.
351 351 only to call in exception handler. returns true if traceback
352 352 printed.'''
353 353 if self.traceback:
354 354 traceback.print_exc()
355 355 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now