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