##// END OF EJS Templates
expand the path if destination is not a directory
Benoit Boissinot -
r2624:46e52bbb default
parent child Browse files
Show More
@@ -1,363 +1,363
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 "://" not in path and not os.path.isabs(path):
79 if path and "://" not in path 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 configlist(self, section, name, default=None):
98 def configlist(self, section, name, default=None):
99 """Return a list of comma/space separated strings"""
99 """Return a list of comma/space separated strings"""
100 result = self.config(section, name)
100 result = self.config(section, name)
101 if result is None:
101 if result is None:
102 result = default or []
102 result = default or []
103 if isinstance(result, basestring):
103 if isinstance(result, basestring):
104 result = result.replace(",", " ").split()
104 result = result.replace(",", " ").split()
105 return result
105 return result
106
106
107 def configbool(self, section, name, default=False):
107 def configbool(self, section, name, default=False):
108 if self.overlay.has_key((section, name)):
108 if self.overlay.has_key((section, name)):
109 return self.overlay[(section, name)]
109 return self.overlay[(section, name)]
110 if self.cdata.has_option(section, name):
110 if self.cdata.has_option(section, name):
111 try:
111 try:
112 return self.cdata.getboolean(section, name)
112 return self.cdata.getboolean(section, name)
113 except ConfigParser.InterpolationError, inst:
113 except ConfigParser.InterpolationError, inst:
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
114 raise util.Abort(_("Error in configuration:\n%s") % inst)
115 if self.parentui is None:
115 if self.parentui is None:
116 return default
116 return default
117 else:
117 else:
118 return self.parentui.configbool(section, name, default)
118 return self.parentui.configbool(section, name, default)
119
119
120 def has_config(self, section):
120 def has_config(self, section):
121 '''tell whether section exists in config.'''
121 '''tell whether section exists in config.'''
122 return self.cdata.has_section(section)
122 return self.cdata.has_section(section)
123
123
124 def configitems(self, section):
124 def configitems(self, section):
125 items = {}
125 items = {}
126 if self.parentui is not None:
126 if self.parentui is not None:
127 items = dict(self.parentui.configitems(section))
127 items = dict(self.parentui.configitems(section))
128 if self.cdata.has_section(section):
128 if self.cdata.has_section(section):
129 try:
129 try:
130 items.update(dict(self.cdata.items(section)))
130 items.update(dict(self.cdata.items(section)))
131 except ConfigParser.InterpolationError, inst:
131 except ConfigParser.InterpolationError, inst:
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
132 raise util.Abort(_("Error in configuration:\n%s") % inst)
133 x = items.items()
133 x = items.items()
134 x.sort()
134 x.sort()
135 return x
135 return x
136
136
137 def walkconfig(self, seen=None):
137 def walkconfig(self, seen=None):
138 if seen is None:
138 if seen is None:
139 seen = {}
139 seen = {}
140 for (section, name), value in self.overlay.iteritems():
140 for (section, name), value in self.overlay.iteritems():
141 yield section, name, value
141 yield section, name, value
142 seen[section, name] = 1
142 seen[section, name] = 1
143 for section in self.cdata.sections():
143 for section in self.cdata.sections():
144 for name, value in self.cdata.items(section):
144 for name, value in self.cdata.items(section):
145 if (section, name) in seen: continue
145 if (section, name) in seen: continue
146 yield section, name, value.replace('\n', '\\n')
146 yield section, name, value.replace('\n', '\\n')
147 seen[section, name] = 1
147 seen[section, name] = 1
148 if self.parentui is not None:
148 if self.parentui is not None:
149 for parent in self.parentui.walkconfig(seen):
149 for parent in self.parentui.walkconfig(seen):
150 yield parent
150 yield parent
151
151
152 def extensions(self):
152 def extensions(self):
153 result = self.configitems("extensions")
153 result = self.configitems("extensions")
154 for i, (key, value) in enumerate(result):
154 for i, (key, value) in enumerate(result):
155 if value:
155 if value:
156 result[i] = (key, os.path.expanduser(value))
156 result[i] = (key, os.path.expanduser(value))
157 return result
157 return result
158
158
159 def hgignorefiles(self):
159 def hgignorefiles(self):
160 result = []
160 result = []
161 for key, value in self.configitems("ui"):
161 for key, value in self.configitems("ui"):
162 if key == 'ignore' or key.startswith('ignore.'):
162 if key == 'ignore' or key.startswith('ignore.'):
163 result.append(os.path.expanduser(value))
163 result.append(os.path.expanduser(value))
164 return result
164 return result
165
165
166 def configrevlog(self):
166 def configrevlog(self):
167 result = {}
167 result = {}
168 for key, value in self.configitems("revlog"):
168 for key, value in self.configitems("revlog"):
169 result[key.lower()] = value
169 result[key.lower()] = value
170 return result
170 return result
171
171
172 def diffopts(self):
172 def diffopts(self):
173 if self.diffcache:
173 if self.diffcache:
174 return self.diffcache
174 return self.diffcache
175 result = {'showfunc': True, 'ignorews': False,
175 result = {'showfunc': True, 'ignorews': False,
176 'ignorewsamount': False, 'ignoreblanklines': False}
176 'ignorewsamount': False, 'ignoreblanklines': False}
177 for key, value in self.configitems("diff"):
177 for key, value in self.configitems("diff"):
178 if value:
178 if value:
179 result[key.lower()] = (value.lower() == 'true')
179 result[key.lower()] = (value.lower() == 'true')
180 self.diffcache = result
180 self.diffcache = result
181 return result
181 return result
182
182
183 def username(self):
183 def username(self):
184 """Return default username to be used in commits.
184 """Return default username to be used in commits.
185
185
186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
186 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
187 and stop searching if one of these is set.
187 and stop searching if one of these is set.
188 Abort if found username is an empty string to force specifying
188 Abort if found username is an empty string to force specifying
189 the commit user elsewhere, e.g. with line option or repo hgrc.
189 the commit user elsewhere, e.g. with line option or repo hgrc.
190 If not found, use ($LOGNAME or $USER or $LNAME or
190 If not found, use ($LOGNAME or $USER or $LNAME or
191 $USERNAME) +"@full.hostname".
191 $USERNAME) +"@full.hostname".
192 """
192 """
193 user = os.environ.get("HGUSER")
193 user = os.environ.get("HGUSER")
194 if user is None:
194 if user is None:
195 user = self.config("ui", "username")
195 user = self.config("ui", "username")
196 if user is None:
196 if user is None:
197 user = os.environ.get("EMAIL")
197 user = os.environ.get("EMAIL")
198 if user is None:
198 if user is None:
199 try:
199 try:
200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
200 user = '%s@%s' % (getpass.getuser(), socket.getfqdn())
201 except KeyError:
201 except KeyError:
202 raise util.Abort(_("Please specify a username."))
202 raise util.Abort(_("Please specify a username."))
203 return user
203 return user
204
204
205 def shortuser(self, user):
205 def shortuser(self, user):
206 """Return a short representation of a user name or email address."""
206 """Return a short representation of a user name or email address."""
207 if not self.verbose: user = util.shortuser(user)
207 if not self.verbose: user = util.shortuser(user)
208 return user
208 return user
209
209
210 def expandpath(self, loc, default=None):
210 def expandpath(self, loc, default=None):
211 """Return repository location relative to cwd or from [paths]"""
211 """Return repository location relative to cwd or from [paths]"""
212 if "://" in loc or os.path.exists(loc):
212 if "://" in loc or os.path.isdir(loc):
213 return loc
213 return loc
214
214
215 path = self.config("paths", loc)
215 path = self.config("paths", loc)
216 if not path and default is not None:
216 if not path and default is not None:
217 path = self.config("paths", default)
217 path = self.config("paths", default)
218 return path or loc
218 return path or loc
219
219
220 def setconfig_remoteopts(self, **opts):
220 def setconfig_remoteopts(self, **opts):
221 if opts.get('ssh'):
221 if opts.get('ssh'):
222 self.setconfig("ui", "ssh", opts['ssh'])
222 self.setconfig("ui", "ssh", opts['ssh'])
223 if opts.get('remotecmd'):
223 if opts.get('remotecmd'):
224 self.setconfig("ui", "remotecmd", opts['remotecmd'])
224 self.setconfig("ui", "remotecmd", opts['remotecmd'])
225
225
226 def write(self, *args):
226 def write(self, *args):
227 if self.header:
227 if self.header:
228 if self.header != self.prev_header:
228 if self.header != self.prev_header:
229 self.prev_header = self.header
229 self.prev_header = self.header
230 self.write(*self.header)
230 self.write(*self.header)
231 self.header = []
231 self.header = []
232 for a in args:
232 for a in args:
233 sys.stdout.write(str(a))
233 sys.stdout.write(str(a))
234
234
235 def write_header(self, *args):
235 def write_header(self, *args):
236 for a in args:
236 for a in args:
237 self.header.append(str(a))
237 self.header.append(str(a))
238
238
239 def write_err(self, *args):
239 def write_err(self, *args):
240 try:
240 try:
241 if not sys.stdout.closed: sys.stdout.flush()
241 if not sys.stdout.closed: sys.stdout.flush()
242 for a in args:
242 for a in args:
243 sys.stderr.write(str(a))
243 sys.stderr.write(str(a))
244 except IOError, inst:
244 except IOError, inst:
245 if inst.errno != errno.EPIPE:
245 if inst.errno != errno.EPIPE:
246 raise
246 raise
247
247
248 def flush(self):
248 def flush(self):
249 try: sys.stdout.flush()
249 try: sys.stdout.flush()
250 except: pass
250 except: pass
251 try: sys.stderr.flush()
251 try: sys.stderr.flush()
252 except: pass
252 except: pass
253
253
254 def readline(self):
254 def readline(self):
255 return sys.stdin.readline()[:-1]
255 return sys.stdin.readline()[:-1]
256 def prompt(self, msg, pat=None, default="y"):
256 def prompt(self, msg, pat=None, default="y"):
257 if not self.interactive: return default
257 if not self.interactive: return default
258 while 1:
258 while 1:
259 self.write(msg, " ")
259 self.write(msg, " ")
260 r = self.readline()
260 r = self.readline()
261 if not pat or re.match(pat, r):
261 if not pat or re.match(pat, r):
262 return r
262 return r
263 else:
263 else:
264 self.write(_("unrecognized response\n"))
264 self.write(_("unrecognized response\n"))
265 def getpass(self, prompt=None, default=None):
265 def getpass(self, prompt=None, default=None):
266 if not self.interactive: return default
266 if not self.interactive: return default
267 return getpass.getpass(prompt or _('password: '))
267 return getpass.getpass(prompt or _('password: '))
268 def status(self, *msg):
268 def status(self, *msg):
269 if not self.quiet: self.write(*msg)
269 if not self.quiet: self.write(*msg)
270 def warn(self, *msg):
270 def warn(self, *msg):
271 self.write_err(*msg)
271 self.write_err(*msg)
272 def note(self, *msg):
272 def note(self, *msg):
273 if self.verbose: self.write(*msg)
273 if self.verbose: self.write(*msg)
274 def debug(self, *msg):
274 def debug(self, *msg):
275 if self.debugflag: self.write(*msg)
275 if self.debugflag: self.write(*msg)
276 def edit(self, text, user):
276 def edit(self, text, user):
277 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
277 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
278 text=True)
278 text=True)
279 try:
279 try:
280 f = os.fdopen(fd, "w")
280 f = os.fdopen(fd, "w")
281 f.write(text)
281 f.write(text)
282 f.close()
282 f.close()
283
283
284 editor = (os.environ.get("HGEDITOR") or
284 editor = (os.environ.get("HGEDITOR") or
285 self.config("ui", "editor") or
285 self.config("ui", "editor") or
286 os.environ.get("EDITOR", "vi"))
286 os.environ.get("EDITOR", "vi"))
287
287
288 util.system("%s \"%s\"" % (editor, name),
288 util.system("%s \"%s\"" % (editor, name),
289 environ={'HGUSER': user},
289 environ={'HGUSER': user},
290 onerr=util.Abort, errprefix=_("edit failed"))
290 onerr=util.Abort, errprefix=_("edit failed"))
291
291
292 f = open(name)
292 f = open(name)
293 t = f.read()
293 t = f.read()
294 f.close()
294 f.close()
295 t = re.sub("(?m)^HG:.*\n", "", t)
295 t = re.sub("(?m)^HG:.*\n", "", t)
296 finally:
296 finally:
297 os.unlink(name)
297 os.unlink(name)
298
298
299 return t
299 return t
300
300
301 def sendmail(self):
301 def sendmail(self):
302 '''send mail message. object returned has one method, sendmail.
302 '''send mail message. object returned has one method, sendmail.
303 call as sendmail(sender, list-of-recipients, msg).'''
303 call as sendmail(sender, list-of-recipients, msg).'''
304
304
305 def smtp():
305 def smtp():
306 '''send mail using smtp.'''
306 '''send mail using smtp.'''
307
307
308 local_hostname = self.config('smtp', 'local_hostname')
308 local_hostname = self.config('smtp', 'local_hostname')
309 s = smtplib.SMTP(local_hostname=local_hostname)
309 s = smtplib.SMTP(local_hostname=local_hostname)
310 mailhost = self.config('smtp', 'host')
310 mailhost = self.config('smtp', 'host')
311 if not mailhost:
311 if not mailhost:
312 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
312 raise util.Abort(_('no [smtp]host in hgrc - cannot send mail'))
313 mailport = int(self.config('smtp', 'port', 25))
313 mailport = int(self.config('smtp', 'port', 25))
314 self.note(_('sending mail: smtp host %s, port %s\n') %
314 self.note(_('sending mail: smtp host %s, port %s\n') %
315 (mailhost, mailport))
315 (mailhost, mailport))
316 s.connect(host=mailhost, port=mailport)
316 s.connect(host=mailhost, port=mailport)
317 if self.configbool('smtp', 'tls'):
317 if self.configbool('smtp', 'tls'):
318 self.note(_('(using tls)\n'))
318 self.note(_('(using tls)\n'))
319 s.ehlo()
319 s.ehlo()
320 s.starttls()
320 s.starttls()
321 s.ehlo()
321 s.ehlo()
322 username = self.config('smtp', 'username')
322 username = self.config('smtp', 'username')
323 password = self.config('smtp', 'password')
323 password = self.config('smtp', 'password')
324 if username and password:
324 if username and password:
325 self.note(_('(authenticating to mail server as %s)\n') %
325 self.note(_('(authenticating to mail server as %s)\n') %
326 (username))
326 (username))
327 s.login(username, password)
327 s.login(username, password)
328 return s
328 return s
329
329
330 class sendmail(object):
330 class sendmail(object):
331 '''send mail using sendmail.'''
331 '''send mail using sendmail.'''
332
332
333 def __init__(self, ui, program):
333 def __init__(self, ui, program):
334 self.ui = ui
334 self.ui = ui
335 self.program = program
335 self.program = program
336
336
337 def sendmail(self, sender, recipients, msg):
337 def sendmail(self, sender, recipients, msg):
338 cmdline = '%s -f %s %s' % (
338 cmdline = '%s -f %s %s' % (
339 self.program, templater.email(sender),
339 self.program, templater.email(sender),
340 ' '.join(map(templater.email, recipients)))
340 ' '.join(map(templater.email, recipients)))
341 self.ui.note(_('sending mail: %s\n') % cmdline)
341 self.ui.note(_('sending mail: %s\n') % cmdline)
342 fp = os.popen(cmdline, 'w')
342 fp = os.popen(cmdline, 'w')
343 fp.write(msg)
343 fp.write(msg)
344 ret = fp.close()
344 ret = fp.close()
345 if ret:
345 if ret:
346 raise util.Abort('%s %s' % (
346 raise util.Abort('%s %s' % (
347 os.path.basename(self.program.split(None, 1)[0]),
347 os.path.basename(self.program.split(None, 1)[0]),
348 util.explain_exit(ret)[0]))
348 util.explain_exit(ret)[0]))
349
349
350 method = self.config('email', 'method', 'smtp')
350 method = self.config('email', 'method', 'smtp')
351 if method == 'smtp':
351 if method == 'smtp':
352 mail = smtp()
352 mail = smtp()
353 else:
353 else:
354 mail = sendmail(self, method)
354 mail = sendmail(self, method)
355 return mail
355 return mail
356
356
357 def print_exc(self):
357 def print_exc(self):
358 '''print exception traceback if traceback printing enabled.
358 '''print exception traceback if traceback printing enabled.
359 only to call in exception handler. returns true if traceback
359 only to call in exception handler. returns true if traceback
360 printed.'''
360 printed.'''
361 if self.traceback:
361 if self.traceback:
362 traceback.print_exc()
362 traceback.print_exc()
363 return self.traceback
363 return self.traceback
General Comments 0
You need to be logged in to leave comments. Login now