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