##// END OF EJS Templates
make ui.expandpath better with default path.
Vadim Gelfer -
r2495:4a2a4d98 default
parent child Browse files
Show More
@@ -1,343 +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 return self.config("paths", loc, default or loc)
205 path = self.config("paths", loc)
206 if not path and default is not None:
207 path = self.config("paths", default)
208 return path
206
209
207 def write(self, *args):
210 def write(self, *args):
208 if self.header:
211 if self.header:
209 if self.header != self.prev_header:
212 if self.header != self.prev_header:
210 self.prev_header = self.header
213 self.prev_header = self.header
211 self.write(*self.header)
214 self.write(*self.header)
212 self.header = []
215 self.header = []
213 for a in args:
216 for a in args:
214 sys.stdout.write(str(a))
217 sys.stdout.write(str(a))
215
218
216 def write_header(self, *args):
219 def write_header(self, *args):
217 for a in args:
220 for a in args:
218 self.header.append(str(a))
221 self.header.append(str(a))
219
222
220 def write_err(self, *args):
223 def write_err(self, *args):
221 try:
224 try:
222 if not sys.stdout.closed: sys.stdout.flush()
225 if not sys.stdout.closed: sys.stdout.flush()
223 for a in args:
226 for a in args:
224 sys.stderr.write(str(a))
227 sys.stderr.write(str(a))
225 except IOError, inst:
228 except IOError, inst:
226 if inst.errno != errno.EPIPE:
229 if inst.errno != errno.EPIPE:
227 raise
230 raise
228
231
229 def flush(self):
232 def flush(self):
230 try: sys.stdout.flush()
233 try: sys.stdout.flush()
231 except: pass
234 except: pass
232 try: sys.stderr.flush()
235 try: sys.stderr.flush()
233 except: pass
236 except: pass
234
237
235 def readline(self):
238 def readline(self):
236 return sys.stdin.readline()[:-1]
239 return sys.stdin.readline()[:-1]
237 def prompt(self, msg, pat=None, default="y"):
240 def prompt(self, msg, pat=None, default="y"):
238 if not self.interactive: return default
241 if not self.interactive: return default
239 while 1:
242 while 1:
240 self.write(msg, " ")
243 self.write(msg, " ")
241 r = self.readline()
244 r = self.readline()
242 if not pat or re.match(pat, r):
245 if not pat or re.match(pat, r):
243 return r
246 return r
244 else:
247 else:
245 self.write(_("unrecognized response\n"))
248 self.write(_("unrecognized response\n"))
246 def getpass(self, prompt=None, default=None):
249 def getpass(self, prompt=None, default=None):
247 if not self.interactive: return default
250 if not self.interactive: return default
248 return getpass.getpass(prompt or _('password: '))
251 return getpass.getpass(prompt or _('password: '))
249 def status(self, *msg):
252 def status(self, *msg):
250 if not self.quiet: self.write(*msg)
253 if not self.quiet: self.write(*msg)
251 def warn(self, *msg):
254 def warn(self, *msg):
252 self.write_err(*msg)
255 self.write_err(*msg)
253 def note(self, *msg):
256 def note(self, *msg):
254 if self.verbose: self.write(*msg)
257 if self.verbose: self.write(*msg)
255 def debug(self, *msg):
258 def debug(self, *msg):
256 if self.debugflag: self.write(*msg)
259 if self.debugflag: self.write(*msg)
257 def edit(self, text, user):
260 def edit(self, text, user):
258 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
261 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
259 text=True)
262 text=True)
260 try:
263 try:
261 f = os.fdopen(fd, "w")
264 f = os.fdopen(fd, "w")
262 f.write(text)
265 f.write(text)
263 f.close()
266 f.close()
264
267
265 editor = (os.environ.get("HGEDITOR") or
268 editor = (os.environ.get("HGEDITOR") or
266 self.config("ui", "editor") or
269 self.config("ui", "editor") or
267 os.environ.get("EDITOR", "vi"))
270 os.environ.get("EDITOR", "vi"))
268
271
269 util.system("%s \"%s\"" % (editor, name),
272 util.system("%s \"%s\"" % (editor, name),
270 environ={'HGUSER': user},
273 environ={'HGUSER': user},
271 onerr=util.Abort, errprefix=_("edit failed"))
274 onerr=util.Abort, errprefix=_("edit failed"))
272
275
273 f = open(name)
276 f = open(name)
274 t = f.read()
277 t = f.read()
275 f.close()
278 f.close()
276 t = re.sub("(?m)^HG:.*\n", "", t)
279 t = re.sub("(?m)^HG:.*\n", "", t)
277 finally:
280 finally:
278 os.unlink(name)
281 os.unlink(name)
279
282
280 return t
283 return t
281
284
282 def sendmail(self):
285 def sendmail(self):
283 '''send mail message. object returned has one method, sendmail.
286 '''send mail message. object returned has one method, sendmail.
284 call as sendmail(sender, list-of-recipients, msg).'''
287 call as sendmail(sender, list-of-recipients, msg).'''
285
288
286 def smtp():
289 def smtp():
287 '''send mail using smtp.'''
290 '''send mail using smtp.'''
288
291
289 s = smtplib.SMTP()
292 s = smtplib.SMTP()
290 mailhost = self.config('smtp', 'host')
293 mailhost = self.config('smtp', 'host')
291 if not mailhost:
294 if not mailhost:
292 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
295 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
293 mailport = int(self.config('smtp', 'port', 25))
296 mailport = int(self.config('smtp', 'port', 25))
294 self.note(_('sending mail: smtp host %s, port %s\n') %
297 self.note(_('sending mail: smtp host %s, port %s\n') %
295 (mailhost, mailport))
298 (mailhost, mailport))
296 s.connect(host=mailhost, port=mailport)
299 s.connect(host=mailhost, port=mailport)
297 if self.configbool('smtp', 'tls'):
300 if self.configbool('smtp', 'tls'):
298 self.note(_('(using tls)\n'))
301 self.note(_('(using tls)\n'))
299 s.ehlo()
302 s.ehlo()
300 s.starttls()
303 s.starttls()
301 s.ehlo()
304 s.ehlo()
302 username = self.config('smtp', 'username')
305 username = self.config('smtp', 'username')
303 password = self.config('smtp', 'password')
306 password = self.config('smtp', 'password')
304 if username and password:
307 if username and password:
305 self.note(_('(authenticating to mail server as %s)\n') %
308 self.note(_('(authenticating to mail server as %s)\n') %
306 (username))
309 (username))
307 s.login(username, password)
310 s.login(username, password)
308 return s
311 return s
309
312
310 class sendmail(object):
313 class sendmail(object):
311 '''send mail using sendmail.'''
314 '''send mail using sendmail.'''
312
315
313 def __init__(self, ui, program):
316 def __init__(self, ui, program):
314 self.ui = ui
317 self.ui = ui
315 self.program = program
318 self.program = program
316
319
317 def sendmail(self, sender, recipients, msg):
320 def sendmail(self, sender, recipients, msg):
318 cmdline = '%s -f %s %s' % (
321 cmdline = '%s -f %s %s' % (
319 self.program, templater.email(sender),
322 self.program, templater.email(sender),
320 ' '.join(map(templater.email, recipients)))
323 ' '.join(map(templater.email, recipients)))
321 self.ui.note(_('sending mail: %s\n') % cmdline)
324 self.ui.note(_('sending mail: %s\n') % cmdline)
322 fp = os.popen(cmdline, 'w')
325 fp = os.popen(cmdline, 'w')
323 fp.write(msg)
326 fp.write(msg)
324 ret = fp.close()
327 ret = fp.close()
325 if ret:
328 if ret:
326 raise util.Abort('%s %s' % (
329 raise util.Abort('%s %s' % (
327 os.path.basename(self.program.split(None, 1)[0]),
330 os.path.basename(self.program.split(None, 1)[0]),
328 util.explain_exit(ret)[0]))
331 util.explain_exit(ret)[0]))
329
332
330 method = self.config('email', 'method', 'smtp')
333 method = self.config('email', 'method', 'smtp')
331 if method == 'smtp':
334 if method == 'smtp':
332 mail = smtp()
335 mail = smtp()
333 else:
336 else:
334 mail = sendmail(self, method)
337 mail = sendmail(self, method)
335 return mail
338 return mail
336
339
337 def print_exc(self):
340 def print_exc(self):
338 '''print exception traceback if traceback printing enabled.
341 '''print exception traceback if traceback printing enabled.
339 only to call in exception handler. returns true if traceback
342 only to call in exception handler. returns true if traceback
340 printed.'''
343 printed.'''
341 if self.traceback:
344 if self.traceback:
342 traceback.print_exc()
345 traceback.print_exc()
343 return self.traceback
346 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now