##// END OF EJS Templates
config: have a way to backup and restore value in config...
Pierre-Yves David -
r15919:69e792cf default
parent child Browse files
Show More
@@ -1,149 +1,176
1 # config.py - configuration parsing for Mercurial
1 # config.py - configuration parsing for Mercurial
2 #
2 #
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import error, util
9 import error, util
10 import re, os, errno
10 import re, os, errno
11
11
12 class sortdict(dict):
12 class sortdict(dict):
13 'a simple sorted dictionary'
13 'a simple sorted dictionary'
14 def __init__(self, data=None):
14 def __init__(self, data=None):
15 self._list = []
15 self._list = []
16 if data:
16 if data:
17 self.update(data)
17 self.update(data)
18 def copy(self):
18 def copy(self):
19 return sortdict(self)
19 return sortdict(self)
20 def __setitem__(self, key, val):
20 def __setitem__(self, key, val):
21 if key in self:
21 if key in self:
22 self._list.remove(key)
22 self._list.remove(key)
23 self._list.append(key)
23 self._list.append(key)
24 dict.__setitem__(self, key, val)
24 dict.__setitem__(self, key, val)
25 def __iter__(self):
25 def __iter__(self):
26 return self._list.__iter__()
26 return self._list.__iter__()
27 def update(self, src):
27 def update(self, src):
28 for k in src:
28 for k in src:
29 self[k] = src[k]
29 self[k] = src[k]
30 def clear(self):
30 def clear(self):
31 dict.clear(self)
31 dict.clear(self)
32 self._list = []
32 self._list = []
33 def items(self):
33 def items(self):
34 return [(k, self[k]) for k in self._list]
34 return [(k, self[k]) for k in self._list]
35 def __delitem__(self, key):
35 def __delitem__(self, key):
36 dict.__delitem__(self, key)
36 dict.__delitem__(self, key)
37 self._list.remove(key)
37 self._list.remove(key)
38
38
39 class config(object):
39 class config(object):
40 def __init__(self, data=None):
40 def __init__(self, data=None):
41 self._data = {}
41 self._data = {}
42 self._source = {}
42 self._source = {}
43 if data:
43 if data:
44 for k in data._data:
44 for k in data._data:
45 self._data[k] = data[k].copy()
45 self._data[k] = data[k].copy()
46 self._source = data._source.copy()
46 self._source = data._source.copy()
47 def copy(self):
47 def copy(self):
48 return config(self)
48 return config(self)
49 def __contains__(self, section):
49 def __contains__(self, section):
50 return section in self._data
50 return section in self._data
51 def __getitem__(self, section):
51 def __getitem__(self, section):
52 return self._data.get(section, {})
52 return self._data.get(section, {})
53 def __iter__(self):
53 def __iter__(self):
54 for d in self.sections():
54 for d in self.sections():
55 yield d
55 yield d
56 def update(self, src):
56 def update(self, src):
57 for s in src:
57 for s in src:
58 if s not in self:
58 if s not in self:
59 self._data[s] = sortdict()
59 self._data[s] = sortdict()
60 self._data[s].update(src._data[s])
60 self._data[s].update(src._data[s])
61 self._source.update(src._source)
61 self._source.update(src._source)
62 def get(self, section, item, default=None):
62 def get(self, section, item, default=None):
63 return self._data.get(section, {}).get(item, default)
63 return self._data.get(section, {}).get(item, default)
64
65 def backup(self, section, item):
66 """return a tuple allowing restore to reinstall a previous valuesi
67
68 The main reason we need it is because it handle the "no data" case.
69 """
70 try:
71 value = self._data[section][item]
72 source = self.source(section, item)
73 return (section, item, value, source)
74 except KeyError:
75 return (section, item)
76
64 def source(self, section, item):
77 def source(self, section, item):
65 return self._source.get((section, item), "")
78 return self._source.get((section, item), "")
66 def sections(self):
79 def sections(self):
67 return sorted(self._data.keys())
80 return sorted(self._data.keys())
68 def items(self, section):
81 def items(self, section):
69 return self._data.get(section, {}).items()
82 return self._data.get(section, {}).items()
70 def set(self, section, item, value, source=""):
83 def set(self, section, item, value, source=""):
71 if section not in self:
84 if section not in self:
72 self._data[section] = sortdict()
85 self._data[section] = sortdict()
73 self._data[section][item] = value
86 self._data[section][item] = value
74 self._source[(section, item)] = source
87 self._source[(section, item)] = source
75
88
89 def restore(self, data):
90 """restore data returned by self.backup"""
91 if len(data) == 4:
92 # restore old data
93 section, item, value, source = data
94 self._data[section][item] = value
95 self._source[(section, item)] = source
96 else:
97 # no data before, remove everything
98 section, item = data
99 if section in self._data:
100 del self._data[section][item]
101 self._source.pop((section, item), None)
102
76 def parse(self, src, data, sections=None, remap=None, include=None):
103 def parse(self, src, data, sections=None, remap=None, include=None):
77 sectionre = re.compile(r'\[([^\[]+)\]')
104 sectionre = re.compile(r'\[([^\[]+)\]')
78 itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
105 itemre = re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
79 contre = re.compile(r'\s+(\S|\S.*\S)\s*$')
106 contre = re.compile(r'\s+(\S|\S.*\S)\s*$')
80 emptyre = re.compile(r'(;|#|\s*$)')
107 emptyre = re.compile(r'(;|#|\s*$)')
81 commentre = re.compile(r'(;|#)')
108 commentre = re.compile(r'(;|#)')
82 unsetre = re.compile(r'%unset\s+(\S+)')
109 unsetre = re.compile(r'%unset\s+(\S+)')
83 includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$')
110 includere = re.compile(r'%include\s+(\S|\S.*\S)\s*$')
84 section = ""
111 section = ""
85 item = None
112 item = None
86 line = 0
113 line = 0
87 cont = False
114 cont = False
88
115
89 for l in data.splitlines(True):
116 for l in data.splitlines(True):
90 line += 1
117 line += 1
91 if cont:
118 if cont:
92 if commentre.match(l):
119 if commentre.match(l):
93 continue
120 continue
94 m = contre.match(l)
121 m = contre.match(l)
95 if m:
122 if m:
96 if sections and section not in sections:
123 if sections and section not in sections:
97 continue
124 continue
98 v = self.get(section, item) + "\n" + m.group(1)
125 v = self.get(section, item) + "\n" + m.group(1)
99 self.set(section, item, v, "%s:%d" % (src, line))
126 self.set(section, item, v, "%s:%d" % (src, line))
100 continue
127 continue
101 item = None
128 item = None
102 cont = False
129 cont = False
103 m = includere.match(l)
130 m = includere.match(l)
104 if m:
131 if m:
105 inc = util.expandpath(m.group(1))
132 inc = util.expandpath(m.group(1))
106 base = os.path.dirname(src)
133 base = os.path.dirname(src)
107 inc = os.path.normpath(os.path.join(base, inc))
134 inc = os.path.normpath(os.path.join(base, inc))
108 if include:
135 if include:
109 try:
136 try:
110 include(inc, remap=remap, sections=sections)
137 include(inc, remap=remap, sections=sections)
111 except IOError, inst:
138 except IOError, inst:
112 if inst.errno != errno.ENOENT:
139 if inst.errno != errno.ENOENT:
113 raise error.ParseError(_("cannot include %s (%s)")
140 raise error.ParseError(_("cannot include %s (%s)")
114 % (inc, inst.strerror),
141 % (inc, inst.strerror),
115 "%s:%s" % (src, line))
142 "%s:%s" % (src, line))
116 continue
143 continue
117 if emptyre.match(l):
144 if emptyre.match(l):
118 continue
145 continue
119 m = sectionre.match(l)
146 m = sectionre.match(l)
120 if m:
147 if m:
121 section = m.group(1)
148 section = m.group(1)
122 if remap:
149 if remap:
123 section = remap.get(section, section)
150 section = remap.get(section, section)
124 if section not in self:
151 if section not in self:
125 self._data[section] = sortdict()
152 self._data[section] = sortdict()
126 continue
153 continue
127 m = itemre.match(l)
154 m = itemre.match(l)
128 if m:
155 if m:
129 item = m.group(1)
156 item = m.group(1)
130 cont = True
157 cont = True
131 if sections and section not in sections:
158 if sections and section not in sections:
132 continue
159 continue
133 self.set(section, item, m.group(2), "%s:%d" % (src, line))
160 self.set(section, item, m.group(2), "%s:%d" % (src, line))
134 continue
161 continue
135 m = unsetre.match(l)
162 m = unsetre.match(l)
136 if m:
163 if m:
137 name = m.group(1)
164 name = m.group(1)
138 if sections and section not in sections:
165 if sections and section not in sections:
139 continue
166 continue
140 if self.get(section, name) is not None:
167 if self.get(section, name) is not None:
141 del self._data[section][name]
168 del self._data[section][name]
142 continue
169 continue
143
170
144 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
171 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
145
172
146 def read(self, path, fp=None, sections=None, remap=None):
173 def read(self, path, fp=None, sections=None, remap=None):
147 if not fp:
174 if not fp:
148 fp = util.posixfile(path)
175 fp = util.posixfile(path)
149 self.parse(path, fp.read(), sections, remap, self.read)
176 self.parse(path, fp.read(), sections, remap, self.read)
@@ -1,734 +1,743
1 # ui.py - user interface bits for mercurial
1 # ui.py - user interface bits for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from i18n import _
8 from i18n import _
9 import errno, getpass, os, socket, sys, tempfile, traceback
9 import errno, getpass, os, socket, sys, tempfile, traceback
10 import config, scmutil, util, error
10 import config, scmutil, util, error
11
11
12 class ui(object):
12 class ui(object):
13 def __init__(self, src=None):
13 def __init__(self, src=None):
14 self._buffers = []
14 self._buffers = []
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
15 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
16 self._reportuntrusted = True
16 self._reportuntrusted = True
17 self._ocfg = config.config() # overlay
17 self._ocfg = config.config() # overlay
18 self._tcfg = config.config() # trusted
18 self._tcfg = config.config() # trusted
19 self._ucfg = config.config() # untrusted
19 self._ucfg = config.config() # untrusted
20 self._trustusers = set()
20 self._trustusers = set()
21 self._trustgroups = set()
21 self._trustgroups = set()
22
22
23 if src:
23 if src:
24 self.fout = src.fout
24 self.fout = src.fout
25 self.ferr = src.ferr
25 self.ferr = src.ferr
26 self.fin = src.fin
26 self.fin = src.fin
27
27
28 self._tcfg = src._tcfg.copy()
28 self._tcfg = src._tcfg.copy()
29 self._ucfg = src._ucfg.copy()
29 self._ucfg = src._ucfg.copy()
30 self._ocfg = src._ocfg.copy()
30 self._ocfg = src._ocfg.copy()
31 self._trustusers = src._trustusers.copy()
31 self._trustusers = src._trustusers.copy()
32 self._trustgroups = src._trustgroups.copy()
32 self._trustgroups = src._trustgroups.copy()
33 self.environ = src.environ
33 self.environ = src.environ
34 self.fixconfig()
34 self.fixconfig()
35 else:
35 else:
36 self.fout = sys.stdout
36 self.fout = sys.stdout
37 self.ferr = sys.stderr
37 self.ferr = sys.stderr
38 self.fin = sys.stdin
38 self.fin = sys.stdin
39
39
40 # shared read-only environment
40 # shared read-only environment
41 self.environ = os.environ
41 self.environ = os.environ
42 # we always trust global config files
42 # we always trust global config files
43 for f in scmutil.rcpath():
43 for f in scmutil.rcpath():
44 self.readconfig(f, trust=True)
44 self.readconfig(f, trust=True)
45
45
46 def copy(self):
46 def copy(self):
47 return self.__class__(self)
47 return self.__class__(self)
48
48
49 def _trusted(self, fp, f):
49 def _trusted(self, fp, f):
50 st = util.fstat(fp)
50 st = util.fstat(fp)
51 if util.isowner(st):
51 if util.isowner(st):
52 return True
52 return True
53
53
54 tusers, tgroups = self._trustusers, self._trustgroups
54 tusers, tgroups = self._trustusers, self._trustgroups
55 if '*' in tusers or '*' in tgroups:
55 if '*' in tusers or '*' in tgroups:
56 return True
56 return True
57
57
58 user = util.username(st.st_uid)
58 user = util.username(st.st_uid)
59 group = util.groupname(st.st_gid)
59 group = util.groupname(st.st_gid)
60 if user in tusers or group in tgroups or user == util.username():
60 if user in tusers or group in tgroups or user == util.username():
61 return True
61 return True
62
62
63 if self._reportuntrusted:
63 if self._reportuntrusted:
64 self.warn(_('Not trusting file %s from untrusted '
64 self.warn(_('Not trusting file %s from untrusted '
65 'user %s, group %s\n') % (f, user, group))
65 'user %s, group %s\n') % (f, user, group))
66 return False
66 return False
67
67
68 def readconfig(self, filename, root=None, trust=False,
68 def readconfig(self, filename, root=None, trust=False,
69 sections=None, remap=None):
69 sections=None, remap=None):
70 try:
70 try:
71 fp = open(filename)
71 fp = open(filename)
72 except IOError:
72 except IOError:
73 if not sections: # ignore unless we were looking for something
73 if not sections: # ignore unless we were looking for something
74 return
74 return
75 raise
75 raise
76
76
77 cfg = config.config()
77 cfg = config.config()
78 trusted = sections or trust or self._trusted(fp, filename)
78 trusted = sections or trust or self._trusted(fp, filename)
79
79
80 try:
80 try:
81 cfg.read(filename, fp, sections=sections, remap=remap)
81 cfg.read(filename, fp, sections=sections, remap=remap)
82 fp.close()
82 fp.close()
83 except error.ConfigError, inst:
83 except error.ConfigError, inst:
84 if trusted:
84 if trusted:
85 raise
85 raise
86 self.warn(_("Ignored: %s\n") % str(inst))
86 self.warn(_("Ignored: %s\n") % str(inst))
87
87
88 if self.plain():
88 if self.plain():
89 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
89 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
90 'logtemplate', 'style',
90 'logtemplate', 'style',
91 'traceback', 'verbose'):
91 'traceback', 'verbose'):
92 if k in cfg['ui']:
92 if k in cfg['ui']:
93 del cfg['ui'][k]
93 del cfg['ui'][k]
94 for k, v in cfg.items('defaults'):
94 for k, v in cfg.items('defaults'):
95 del cfg['defaults'][k]
95 del cfg['defaults'][k]
96 # Don't remove aliases from the configuration if in the exceptionlist
96 # Don't remove aliases from the configuration if in the exceptionlist
97 if self.plain('alias'):
97 if self.plain('alias'):
98 for k, v in cfg.items('alias'):
98 for k, v in cfg.items('alias'):
99 del cfg['alias'][k]
99 del cfg['alias'][k]
100
100
101 if trusted:
101 if trusted:
102 self._tcfg.update(cfg)
102 self._tcfg.update(cfg)
103 self._tcfg.update(self._ocfg)
103 self._tcfg.update(self._ocfg)
104 self._ucfg.update(cfg)
104 self._ucfg.update(cfg)
105 self._ucfg.update(self._ocfg)
105 self._ucfg.update(self._ocfg)
106
106
107 if root is None:
107 if root is None:
108 root = os.path.expanduser('~')
108 root = os.path.expanduser('~')
109 self.fixconfig(root=root)
109 self.fixconfig(root=root)
110
110
111 def fixconfig(self, root=None, section=None):
111 def fixconfig(self, root=None, section=None):
112 if section in (None, 'paths'):
112 if section in (None, 'paths'):
113 # expand vars and ~
113 # expand vars and ~
114 # translate paths relative to root (or home) into absolute paths
114 # translate paths relative to root (or home) into absolute paths
115 root = root or os.getcwd()
115 root = root or os.getcwd()
116 for c in self._tcfg, self._ucfg, self._ocfg:
116 for c in self._tcfg, self._ucfg, self._ocfg:
117 for n, p in c.items('paths'):
117 for n, p in c.items('paths'):
118 if not p:
118 if not p:
119 continue
119 continue
120 if '%%' in p:
120 if '%%' in p:
121 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
121 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
122 % (n, p, self.configsource('paths', n)))
122 % (n, p, self.configsource('paths', n)))
123 p = p.replace('%%', '%')
123 p = p.replace('%%', '%')
124 p = util.expandpath(p)
124 p = util.expandpath(p)
125 if not util.hasscheme(p) and not os.path.isabs(p):
125 if not util.hasscheme(p) and not os.path.isabs(p):
126 p = os.path.normpath(os.path.join(root, p))
126 p = os.path.normpath(os.path.join(root, p))
127 c.set("paths", n, p)
127 c.set("paths", n, p)
128
128
129 if section in (None, 'ui'):
129 if section in (None, 'ui'):
130 # update ui options
130 # update ui options
131 self.debugflag = self.configbool('ui', 'debug')
131 self.debugflag = self.configbool('ui', 'debug')
132 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
132 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
133 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
133 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
134 if self.verbose and self.quiet:
134 if self.verbose and self.quiet:
135 self.quiet = self.verbose = False
135 self.quiet = self.verbose = False
136 self._reportuntrusted = self.debugflag or self.configbool("ui",
136 self._reportuntrusted = self.debugflag or self.configbool("ui",
137 "report_untrusted", True)
137 "report_untrusted", True)
138 self.tracebackflag = self.configbool('ui', 'traceback', False)
138 self.tracebackflag = self.configbool('ui', 'traceback', False)
139
139
140 if section in (None, 'trusted'):
140 if section in (None, 'trusted'):
141 # update trust information
141 # update trust information
142 self._trustusers.update(self.configlist('trusted', 'users'))
142 self._trustusers.update(self.configlist('trusted', 'users'))
143 self._trustgroups.update(self.configlist('trusted', 'groups'))
143 self._trustgroups.update(self.configlist('trusted', 'groups'))
144
144
145 def backupconfig(self, section, item):
146 return (self._ocfg.backup(section, item),
147 self._tcfg.backup(section, item),
148 self._ucfg.backup(section, item),)
149 def restoreconfig(self, data):
150 self._ocfg.restore(data[0])
151 self._tcfg.restore(data[1])
152 self._ucfg.restore(data[2])
153
145 def setconfig(self, section, name, value, overlay=True):
154 def setconfig(self, section, name, value, overlay=True):
146 if overlay:
155 if overlay:
147 self._ocfg.set(section, name, value)
156 self._ocfg.set(section, name, value)
148 self._tcfg.set(section, name, value)
157 self._tcfg.set(section, name, value)
149 self._ucfg.set(section, name, value)
158 self._ucfg.set(section, name, value)
150 self.fixconfig(section=section)
159 self.fixconfig(section=section)
151
160
152 def _data(self, untrusted):
161 def _data(self, untrusted):
153 return untrusted and self._ucfg or self._tcfg
162 return untrusted and self._ucfg or self._tcfg
154
163
155 def configsource(self, section, name, untrusted=False):
164 def configsource(self, section, name, untrusted=False):
156 return self._data(untrusted).source(section, name) or 'none'
165 return self._data(untrusted).source(section, name) or 'none'
157
166
158 def config(self, section, name, default=None, untrusted=False):
167 def config(self, section, name, default=None, untrusted=False):
159 if isinstance(name, list):
168 if isinstance(name, list):
160 alternates = name
169 alternates = name
161 else:
170 else:
162 alternates = [name]
171 alternates = [name]
163
172
164 for n in alternates:
173 for n in alternates:
165 value = self._data(untrusted).get(section, name, None)
174 value = self._data(untrusted).get(section, name, None)
166 if value is not None:
175 if value is not None:
167 name = n
176 name = n
168 break
177 break
169 else:
178 else:
170 value = default
179 value = default
171
180
172 if self.debugflag and not untrusted and self._reportuntrusted:
181 if self.debugflag and not untrusted and self._reportuntrusted:
173 uvalue = self._ucfg.get(section, name)
182 uvalue = self._ucfg.get(section, name)
174 if uvalue is not None and uvalue != value:
183 if uvalue is not None and uvalue != value:
175 self.debug("ignoring untrusted configuration option "
184 self.debug("ignoring untrusted configuration option "
176 "%s.%s = %s\n" % (section, name, uvalue))
185 "%s.%s = %s\n" % (section, name, uvalue))
177 return value
186 return value
178
187
179 def configpath(self, section, name, default=None, untrusted=False):
188 def configpath(self, section, name, default=None, untrusted=False):
180 'get a path config item, expanded relative to repo root or config file'
189 'get a path config item, expanded relative to repo root or config file'
181 v = self.config(section, name, default, untrusted)
190 v = self.config(section, name, default, untrusted)
182 if v is None:
191 if v is None:
183 return None
192 return None
184 if not os.path.isabs(v) or "://" not in v:
193 if not os.path.isabs(v) or "://" not in v:
185 src = self.configsource(section, name, untrusted)
194 src = self.configsource(section, name, untrusted)
186 if ':' in src:
195 if ':' in src:
187 base = os.path.dirname(src.rsplit(':')[0])
196 base = os.path.dirname(src.rsplit(':')[0])
188 v = os.path.join(base, os.path.expanduser(v))
197 v = os.path.join(base, os.path.expanduser(v))
189 return v
198 return v
190
199
191 def configbool(self, section, name, default=False, untrusted=False):
200 def configbool(self, section, name, default=False, untrusted=False):
192 """parse a configuration element as a boolean
201 """parse a configuration element as a boolean
193
202
194 >>> u = ui(); s = 'foo'
203 >>> u = ui(); s = 'foo'
195 >>> u.setconfig(s, 'true', 'yes')
204 >>> u.setconfig(s, 'true', 'yes')
196 >>> u.configbool(s, 'true')
205 >>> u.configbool(s, 'true')
197 True
206 True
198 >>> u.setconfig(s, 'false', 'no')
207 >>> u.setconfig(s, 'false', 'no')
199 >>> u.configbool(s, 'false')
208 >>> u.configbool(s, 'false')
200 False
209 False
201 >>> u.configbool(s, 'unknown')
210 >>> u.configbool(s, 'unknown')
202 False
211 False
203 >>> u.configbool(s, 'unknown', True)
212 >>> u.configbool(s, 'unknown', True)
204 True
213 True
205 >>> u.setconfig(s, 'invalid', 'somevalue')
214 >>> u.setconfig(s, 'invalid', 'somevalue')
206 >>> u.configbool(s, 'invalid')
215 >>> u.configbool(s, 'invalid')
207 Traceback (most recent call last):
216 Traceback (most recent call last):
208 ...
217 ...
209 ConfigError: foo.invalid is not a boolean ('somevalue')
218 ConfigError: foo.invalid is not a boolean ('somevalue')
210 """
219 """
211
220
212 v = self.config(section, name, None, untrusted)
221 v = self.config(section, name, None, untrusted)
213 if v is None:
222 if v is None:
214 return default
223 return default
215 if isinstance(v, bool):
224 if isinstance(v, bool):
216 return v
225 return v
217 b = util.parsebool(v)
226 b = util.parsebool(v)
218 if b is None:
227 if b is None:
219 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
228 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
220 % (section, name, v))
229 % (section, name, v))
221 return b
230 return b
222
231
223 def configint(self, section, name, default=None, untrusted=False):
232 def configint(self, section, name, default=None, untrusted=False):
224 """parse a configuration element as an integer
233 """parse a configuration element as an integer
225
234
226 >>> u = ui(); s = 'foo'
235 >>> u = ui(); s = 'foo'
227 >>> u.setconfig(s, 'int1', '42')
236 >>> u.setconfig(s, 'int1', '42')
228 >>> u.configint(s, 'int1')
237 >>> u.configint(s, 'int1')
229 42
238 42
230 >>> u.setconfig(s, 'int2', '-42')
239 >>> u.setconfig(s, 'int2', '-42')
231 >>> u.configint(s, 'int2')
240 >>> u.configint(s, 'int2')
232 -42
241 -42
233 >>> u.configint(s, 'unknown', 7)
242 >>> u.configint(s, 'unknown', 7)
234 7
243 7
235 >>> u.setconfig(s, 'invalid', 'somevalue')
244 >>> u.setconfig(s, 'invalid', 'somevalue')
236 >>> u.configint(s, 'invalid')
245 >>> u.configint(s, 'invalid')
237 Traceback (most recent call last):
246 Traceback (most recent call last):
238 ...
247 ...
239 ConfigError: foo.invalid is not an integer ('somevalue')
248 ConfigError: foo.invalid is not an integer ('somevalue')
240 """
249 """
241
250
242 v = self.config(section, name, None, untrusted)
251 v = self.config(section, name, None, untrusted)
243 if v is None:
252 if v is None:
244 return default
253 return default
245 try:
254 try:
246 return int(v)
255 return int(v)
247 except ValueError:
256 except ValueError:
248 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
257 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
249 % (section, name, v))
258 % (section, name, v))
250
259
251 def configlist(self, section, name, default=None, untrusted=False):
260 def configlist(self, section, name, default=None, untrusted=False):
252 """parse a configuration element as a list of comma/space separated
261 """parse a configuration element as a list of comma/space separated
253 strings
262 strings
254
263
255 >>> u = ui(); s = 'foo'
264 >>> u = ui(); s = 'foo'
256 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
265 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
257 >>> u.configlist(s, 'list1')
266 >>> u.configlist(s, 'list1')
258 ['this', 'is', 'a small', 'test']
267 ['this', 'is', 'a small', 'test']
259 """
268 """
260
269
261 def _parse_plain(parts, s, offset):
270 def _parse_plain(parts, s, offset):
262 whitespace = False
271 whitespace = False
263 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
272 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
264 whitespace = True
273 whitespace = True
265 offset += 1
274 offset += 1
266 if offset >= len(s):
275 if offset >= len(s):
267 return None, parts, offset
276 return None, parts, offset
268 if whitespace:
277 if whitespace:
269 parts.append('')
278 parts.append('')
270 if s[offset] == '"' and not parts[-1]:
279 if s[offset] == '"' and not parts[-1]:
271 return _parse_quote, parts, offset + 1
280 return _parse_quote, parts, offset + 1
272 elif s[offset] == '"' and parts[-1][-1] == '\\':
281 elif s[offset] == '"' and parts[-1][-1] == '\\':
273 parts[-1] = parts[-1][:-1] + s[offset]
282 parts[-1] = parts[-1][:-1] + s[offset]
274 return _parse_plain, parts, offset + 1
283 return _parse_plain, parts, offset + 1
275 parts[-1] += s[offset]
284 parts[-1] += s[offset]
276 return _parse_plain, parts, offset + 1
285 return _parse_plain, parts, offset + 1
277
286
278 def _parse_quote(parts, s, offset):
287 def _parse_quote(parts, s, offset):
279 if offset < len(s) and s[offset] == '"': # ""
288 if offset < len(s) and s[offset] == '"': # ""
280 parts.append('')
289 parts.append('')
281 offset += 1
290 offset += 1
282 while offset < len(s) and (s[offset].isspace() or
291 while offset < len(s) and (s[offset].isspace() or
283 s[offset] == ','):
292 s[offset] == ','):
284 offset += 1
293 offset += 1
285 return _parse_plain, parts, offset
294 return _parse_plain, parts, offset
286
295
287 while offset < len(s) and s[offset] != '"':
296 while offset < len(s) and s[offset] != '"':
288 if (s[offset] == '\\' and offset + 1 < len(s)
297 if (s[offset] == '\\' and offset + 1 < len(s)
289 and s[offset + 1] == '"'):
298 and s[offset + 1] == '"'):
290 offset += 1
299 offset += 1
291 parts[-1] += '"'
300 parts[-1] += '"'
292 else:
301 else:
293 parts[-1] += s[offset]
302 parts[-1] += s[offset]
294 offset += 1
303 offset += 1
295
304
296 if offset >= len(s):
305 if offset >= len(s):
297 real_parts = _configlist(parts[-1])
306 real_parts = _configlist(parts[-1])
298 if not real_parts:
307 if not real_parts:
299 parts[-1] = '"'
308 parts[-1] = '"'
300 else:
309 else:
301 real_parts[0] = '"' + real_parts[0]
310 real_parts[0] = '"' + real_parts[0]
302 parts = parts[:-1]
311 parts = parts[:-1]
303 parts.extend(real_parts)
312 parts.extend(real_parts)
304 return None, parts, offset
313 return None, parts, offset
305
314
306 offset += 1
315 offset += 1
307 while offset < len(s) and s[offset] in [' ', ',']:
316 while offset < len(s) and s[offset] in [' ', ',']:
308 offset += 1
317 offset += 1
309
318
310 if offset < len(s):
319 if offset < len(s):
311 if offset + 1 == len(s) and s[offset] == '"':
320 if offset + 1 == len(s) and s[offset] == '"':
312 parts[-1] += '"'
321 parts[-1] += '"'
313 offset += 1
322 offset += 1
314 else:
323 else:
315 parts.append('')
324 parts.append('')
316 else:
325 else:
317 return None, parts, offset
326 return None, parts, offset
318
327
319 return _parse_plain, parts, offset
328 return _parse_plain, parts, offset
320
329
321 def _configlist(s):
330 def _configlist(s):
322 s = s.rstrip(' ,')
331 s = s.rstrip(' ,')
323 if not s:
332 if not s:
324 return []
333 return []
325 parser, parts, offset = _parse_plain, [''], 0
334 parser, parts, offset = _parse_plain, [''], 0
326 while parser:
335 while parser:
327 parser, parts, offset = parser(parts, s, offset)
336 parser, parts, offset = parser(parts, s, offset)
328 return parts
337 return parts
329
338
330 result = self.config(section, name, untrusted=untrusted)
339 result = self.config(section, name, untrusted=untrusted)
331 if result is None:
340 if result is None:
332 result = default or []
341 result = default or []
333 if isinstance(result, basestring):
342 if isinstance(result, basestring):
334 result = _configlist(result.lstrip(' ,\n'))
343 result = _configlist(result.lstrip(' ,\n'))
335 if result is None:
344 if result is None:
336 result = default or []
345 result = default or []
337 return result
346 return result
338
347
339 def has_section(self, section, untrusted=False):
348 def has_section(self, section, untrusted=False):
340 '''tell whether section exists in config.'''
349 '''tell whether section exists in config.'''
341 return section in self._data(untrusted)
350 return section in self._data(untrusted)
342
351
343 def configitems(self, section, untrusted=False):
352 def configitems(self, section, untrusted=False):
344 items = self._data(untrusted).items(section)
353 items = self._data(untrusted).items(section)
345 if self.debugflag and not untrusted and self._reportuntrusted:
354 if self.debugflag and not untrusted and self._reportuntrusted:
346 for k, v in self._ucfg.items(section):
355 for k, v in self._ucfg.items(section):
347 if self._tcfg.get(section, k) != v:
356 if self._tcfg.get(section, k) != v:
348 self.debug("ignoring untrusted configuration option "
357 self.debug("ignoring untrusted configuration option "
349 "%s.%s = %s\n" % (section, k, v))
358 "%s.%s = %s\n" % (section, k, v))
350 return items
359 return items
351
360
352 def walkconfig(self, untrusted=False):
361 def walkconfig(self, untrusted=False):
353 cfg = self._data(untrusted)
362 cfg = self._data(untrusted)
354 for section in cfg.sections():
363 for section in cfg.sections():
355 for name, value in self.configitems(section, untrusted):
364 for name, value in self.configitems(section, untrusted):
356 yield section, name, value
365 yield section, name, value
357
366
358 def plain(self, feature=None):
367 def plain(self, feature=None):
359 '''is plain mode active?
368 '''is plain mode active?
360
369
361 Plain mode means that all configuration variables which affect
370 Plain mode means that all configuration variables which affect
362 the behavior and output of Mercurial should be
371 the behavior and output of Mercurial should be
363 ignored. Additionally, the output should be stable,
372 ignored. Additionally, the output should be stable,
364 reproducible and suitable for use in scripts or applications.
373 reproducible and suitable for use in scripts or applications.
365
374
366 The only way to trigger plain mode is by setting either the
375 The only way to trigger plain mode is by setting either the
367 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
376 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
368
377
369 The return value can either be
378 The return value can either be
370 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
379 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
371 - True otherwise
380 - True otherwise
372 '''
381 '''
373 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
382 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
374 return False
383 return False
375 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
384 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
376 if feature and exceptions:
385 if feature and exceptions:
377 return feature not in exceptions
386 return feature not in exceptions
378 return True
387 return True
379
388
380 def username(self):
389 def username(self):
381 """Return default username to be used in commits.
390 """Return default username to be used in commits.
382
391
383 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
392 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
384 and stop searching if one of these is set.
393 and stop searching if one of these is set.
385 If not found and ui.askusername is True, ask the user, else use
394 If not found and ui.askusername is True, ask the user, else use
386 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
395 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
387 """
396 """
388 user = os.environ.get("HGUSER")
397 user = os.environ.get("HGUSER")
389 if user is None:
398 if user is None:
390 user = self.config("ui", "username")
399 user = self.config("ui", "username")
391 if user is not None:
400 if user is not None:
392 user = os.path.expandvars(user)
401 user = os.path.expandvars(user)
393 if user is None:
402 if user is None:
394 user = os.environ.get("EMAIL")
403 user = os.environ.get("EMAIL")
395 if user is None and self.configbool("ui", "askusername"):
404 if user is None and self.configbool("ui", "askusername"):
396 user = self.prompt(_("enter a commit username:"), default=None)
405 user = self.prompt(_("enter a commit username:"), default=None)
397 if user is None and not self.interactive():
406 if user is None and not self.interactive():
398 try:
407 try:
399 user = '%s@%s' % (util.getuser(), socket.getfqdn())
408 user = '%s@%s' % (util.getuser(), socket.getfqdn())
400 self.warn(_("No username found, using '%s' instead\n") % user)
409 self.warn(_("No username found, using '%s' instead\n") % user)
401 except KeyError:
410 except KeyError:
402 pass
411 pass
403 if not user:
412 if not user:
404 raise util.Abort(_('no username supplied (see "hg help config")'))
413 raise util.Abort(_('no username supplied (see "hg help config")'))
405 if "\n" in user:
414 if "\n" in user:
406 raise util.Abort(_("username %s contains a newline\n") % repr(user))
415 raise util.Abort(_("username %s contains a newline\n") % repr(user))
407 return user
416 return user
408
417
409 def shortuser(self, user):
418 def shortuser(self, user):
410 """Return a short representation of a user name or email address."""
419 """Return a short representation of a user name or email address."""
411 if not self.verbose:
420 if not self.verbose:
412 user = util.shortuser(user)
421 user = util.shortuser(user)
413 return user
422 return user
414
423
415 def expandpath(self, loc, default=None):
424 def expandpath(self, loc, default=None):
416 """Return repository location relative to cwd or from [paths]"""
425 """Return repository location relative to cwd or from [paths]"""
417 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
426 if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')):
418 return loc
427 return loc
419
428
420 path = self.config('paths', loc)
429 path = self.config('paths', loc)
421 if not path and default is not None:
430 if not path and default is not None:
422 path = self.config('paths', default)
431 path = self.config('paths', default)
423 return path or loc
432 return path or loc
424
433
425 def pushbuffer(self):
434 def pushbuffer(self):
426 self._buffers.append([])
435 self._buffers.append([])
427
436
428 def popbuffer(self, labeled=False):
437 def popbuffer(self, labeled=False):
429 '''pop the last buffer and return the buffered output
438 '''pop the last buffer and return the buffered output
430
439
431 If labeled is True, any labels associated with buffered
440 If labeled is True, any labels associated with buffered
432 output will be handled. By default, this has no effect
441 output will be handled. By default, this has no effect
433 on the output returned, but extensions and GUI tools may
442 on the output returned, but extensions and GUI tools may
434 handle this argument and returned styled output. If output
443 handle this argument and returned styled output. If output
435 is being buffered so it can be captured and parsed or
444 is being buffered so it can be captured and parsed or
436 processed, labeled should not be set to True.
445 processed, labeled should not be set to True.
437 '''
446 '''
438 return "".join(self._buffers.pop())
447 return "".join(self._buffers.pop())
439
448
440 def write(self, *args, **opts):
449 def write(self, *args, **opts):
441 '''write args to output
450 '''write args to output
442
451
443 By default, this method simply writes to the buffer or stdout,
452 By default, this method simply writes to the buffer or stdout,
444 but extensions or GUI tools may override this method,
453 but extensions or GUI tools may override this method,
445 write_err(), popbuffer(), and label() to style output from
454 write_err(), popbuffer(), and label() to style output from
446 various parts of hg.
455 various parts of hg.
447
456
448 An optional keyword argument, "label", can be passed in.
457 An optional keyword argument, "label", can be passed in.
449 This should be a string containing label names separated by
458 This should be a string containing label names separated by
450 space. Label names take the form of "topic.type". For example,
459 space. Label names take the form of "topic.type". For example,
451 ui.debug() issues a label of "ui.debug".
460 ui.debug() issues a label of "ui.debug".
452
461
453 When labeling output for a specific command, a label of
462 When labeling output for a specific command, a label of
454 "cmdname.type" is recommended. For example, status issues
463 "cmdname.type" is recommended. For example, status issues
455 a label of "status.modified" for modified files.
464 a label of "status.modified" for modified files.
456 '''
465 '''
457 if self._buffers:
466 if self._buffers:
458 self._buffers[-1].extend([str(a) for a in args])
467 self._buffers[-1].extend([str(a) for a in args])
459 else:
468 else:
460 for a in args:
469 for a in args:
461 self.fout.write(str(a))
470 self.fout.write(str(a))
462
471
463 def write_err(self, *args, **opts):
472 def write_err(self, *args, **opts):
464 try:
473 try:
465 if not getattr(self.fout, 'closed', False):
474 if not getattr(self.fout, 'closed', False):
466 self.fout.flush()
475 self.fout.flush()
467 for a in args:
476 for a in args:
468 self.ferr.write(str(a))
477 self.ferr.write(str(a))
469 # stderr may be buffered under win32 when redirected to files,
478 # stderr may be buffered under win32 when redirected to files,
470 # including stdout.
479 # including stdout.
471 if not getattr(self.ferr, 'closed', False):
480 if not getattr(self.ferr, 'closed', False):
472 self.ferr.flush()
481 self.ferr.flush()
473 except IOError, inst:
482 except IOError, inst:
474 if inst.errno not in (errno.EPIPE, errno.EIO):
483 if inst.errno not in (errno.EPIPE, errno.EIO):
475 raise
484 raise
476
485
477 def flush(self):
486 def flush(self):
478 try: self.fout.flush()
487 try: self.fout.flush()
479 except: pass
488 except: pass
480 try: self.ferr.flush()
489 try: self.ferr.flush()
481 except: pass
490 except: pass
482
491
483 def interactive(self):
492 def interactive(self):
484 '''is interactive input allowed?
493 '''is interactive input allowed?
485
494
486 An interactive session is a session where input can be reasonably read
495 An interactive session is a session where input can be reasonably read
487 from `sys.stdin'. If this function returns false, any attempt to read
496 from `sys.stdin'. If this function returns false, any attempt to read
488 from stdin should fail with an error, unless a sensible default has been
497 from stdin should fail with an error, unless a sensible default has been
489 specified.
498 specified.
490
499
491 Interactiveness is triggered by the value of the `ui.interactive'
500 Interactiveness is triggered by the value of the `ui.interactive'
492 configuration variable or - if it is unset - when `sys.stdin' points
501 configuration variable or - if it is unset - when `sys.stdin' points
493 to a terminal device.
502 to a terminal device.
494
503
495 This function refers to input only; for output, see `ui.formatted()'.
504 This function refers to input only; for output, see `ui.formatted()'.
496 '''
505 '''
497 i = self.configbool("ui", "interactive", None)
506 i = self.configbool("ui", "interactive", None)
498 if i is None:
507 if i is None:
499 # some environments replace stdin without implementing isatty
508 # some environments replace stdin without implementing isatty
500 # usually those are non-interactive
509 # usually those are non-interactive
501 return util.isatty(self.fin)
510 return util.isatty(self.fin)
502
511
503 return i
512 return i
504
513
505 def termwidth(self):
514 def termwidth(self):
506 '''how wide is the terminal in columns?
515 '''how wide is the terminal in columns?
507 '''
516 '''
508 if 'COLUMNS' in os.environ:
517 if 'COLUMNS' in os.environ:
509 try:
518 try:
510 return int(os.environ['COLUMNS'])
519 return int(os.environ['COLUMNS'])
511 except ValueError:
520 except ValueError:
512 pass
521 pass
513 return util.termwidth()
522 return util.termwidth()
514
523
515 def formatted(self):
524 def formatted(self):
516 '''should formatted output be used?
525 '''should formatted output be used?
517
526
518 It is often desirable to format the output to suite the output medium.
527 It is often desirable to format the output to suite the output medium.
519 Examples of this are truncating long lines or colorizing messages.
528 Examples of this are truncating long lines or colorizing messages.
520 However, this is not often not desirable when piping output into other
529 However, this is not often not desirable when piping output into other
521 utilities, e.g. `grep'.
530 utilities, e.g. `grep'.
522
531
523 Formatted output is triggered by the value of the `ui.formatted'
532 Formatted output is triggered by the value of the `ui.formatted'
524 configuration variable or - if it is unset - when `sys.stdout' points
533 configuration variable or - if it is unset - when `sys.stdout' points
525 to a terminal device. Please note that `ui.formatted' should be
534 to a terminal device. Please note that `ui.formatted' should be
526 considered an implementation detail; it is not intended for use outside
535 considered an implementation detail; it is not intended for use outside
527 Mercurial or its extensions.
536 Mercurial or its extensions.
528
537
529 This function refers to output only; for input, see `ui.interactive()'.
538 This function refers to output only; for input, see `ui.interactive()'.
530 This function always returns false when in plain mode, see `ui.plain()'.
539 This function always returns false when in plain mode, see `ui.plain()'.
531 '''
540 '''
532 if self.plain():
541 if self.plain():
533 return False
542 return False
534
543
535 i = self.configbool("ui", "formatted", None)
544 i = self.configbool("ui", "formatted", None)
536 if i is None:
545 if i is None:
537 # some environments replace stdout without implementing isatty
546 # some environments replace stdout without implementing isatty
538 # usually those are non-interactive
547 # usually those are non-interactive
539 return util.isatty(self.fout)
548 return util.isatty(self.fout)
540
549
541 return i
550 return i
542
551
543 def _readline(self, prompt=''):
552 def _readline(self, prompt=''):
544 if util.isatty(self.fin):
553 if util.isatty(self.fin):
545 try:
554 try:
546 # magically add command line editing support, where
555 # magically add command line editing support, where
547 # available
556 # available
548 import readline
557 import readline
549 # force demandimport to really load the module
558 # force demandimport to really load the module
550 readline.read_history_file
559 readline.read_history_file
551 # windows sometimes raises something other than ImportError
560 # windows sometimes raises something other than ImportError
552 except Exception:
561 except Exception:
553 pass
562 pass
554
563
555 # call write() so output goes through subclassed implementation
564 # call write() so output goes through subclassed implementation
556 # e.g. color extension on Windows
565 # e.g. color extension on Windows
557 self.write(prompt)
566 self.write(prompt)
558
567
559 # instead of trying to emulate raw_input, swap (self.fin,
568 # instead of trying to emulate raw_input, swap (self.fin,
560 # self.fout) with (sys.stdin, sys.stdout)
569 # self.fout) with (sys.stdin, sys.stdout)
561 oldin = sys.stdin
570 oldin = sys.stdin
562 oldout = sys.stdout
571 oldout = sys.stdout
563 sys.stdin = self.fin
572 sys.stdin = self.fin
564 sys.stdout = self.fout
573 sys.stdout = self.fout
565 line = raw_input(' ')
574 line = raw_input(' ')
566 sys.stdin = oldin
575 sys.stdin = oldin
567 sys.stdout = oldout
576 sys.stdout = oldout
568
577
569 # When stdin is in binary mode on Windows, it can cause
578 # When stdin is in binary mode on Windows, it can cause
570 # raw_input() to emit an extra trailing carriage return
579 # raw_input() to emit an extra trailing carriage return
571 if os.linesep == '\r\n' and line and line[-1] == '\r':
580 if os.linesep == '\r\n' and line and line[-1] == '\r':
572 line = line[:-1]
581 line = line[:-1]
573 return line
582 return line
574
583
575 def prompt(self, msg, default="y"):
584 def prompt(self, msg, default="y"):
576 """Prompt user with msg, read response.
585 """Prompt user with msg, read response.
577 If ui is not interactive, the default is returned.
586 If ui is not interactive, the default is returned.
578 """
587 """
579 if not self.interactive():
588 if not self.interactive():
580 self.write(msg, ' ', default, "\n")
589 self.write(msg, ' ', default, "\n")
581 return default
590 return default
582 try:
591 try:
583 r = self._readline(self.label(msg, 'ui.prompt'))
592 r = self._readline(self.label(msg, 'ui.prompt'))
584 if not r:
593 if not r:
585 return default
594 return default
586 return r
595 return r
587 except EOFError:
596 except EOFError:
588 raise util.Abort(_('response expected'))
597 raise util.Abort(_('response expected'))
589
598
590 def promptchoice(self, msg, choices, default=0):
599 def promptchoice(self, msg, choices, default=0):
591 """Prompt user with msg, read response, and ensure it matches
600 """Prompt user with msg, read response, and ensure it matches
592 one of the provided choices. The index of the choice is returned.
601 one of the provided choices. The index of the choice is returned.
593 choices is a sequence of acceptable responses with the format:
602 choices is a sequence of acceptable responses with the format:
594 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
603 ('&None', 'E&xec', 'Sym&link') Responses are case insensitive.
595 If ui is not interactive, the default is returned.
604 If ui is not interactive, the default is returned.
596 """
605 """
597 resps = [s[s.index('&')+1].lower() for s in choices]
606 resps = [s[s.index('&')+1].lower() for s in choices]
598 while True:
607 while True:
599 r = self.prompt(msg, resps[default])
608 r = self.prompt(msg, resps[default])
600 if r.lower() in resps:
609 if r.lower() in resps:
601 return resps.index(r.lower())
610 return resps.index(r.lower())
602 self.write(_("unrecognized response\n"))
611 self.write(_("unrecognized response\n"))
603
612
604 def getpass(self, prompt=None, default=None):
613 def getpass(self, prompt=None, default=None):
605 if not self.interactive():
614 if not self.interactive():
606 return default
615 return default
607 try:
616 try:
608 return getpass.getpass(prompt or _('password: '))
617 return getpass.getpass(prompt or _('password: '))
609 except EOFError:
618 except EOFError:
610 raise util.Abort(_('response expected'))
619 raise util.Abort(_('response expected'))
611 def status(self, *msg, **opts):
620 def status(self, *msg, **opts):
612 '''write status message to output (if ui.quiet is False)
621 '''write status message to output (if ui.quiet is False)
613
622
614 This adds an output label of "ui.status".
623 This adds an output label of "ui.status".
615 '''
624 '''
616 if not self.quiet:
625 if not self.quiet:
617 opts['label'] = opts.get('label', '') + ' ui.status'
626 opts['label'] = opts.get('label', '') + ' ui.status'
618 self.write(*msg, **opts)
627 self.write(*msg, **opts)
619 def warn(self, *msg, **opts):
628 def warn(self, *msg, **opts):
620 '''write warning message to output (stderr)
629 '''write warning message to output (stderr)
621
630
622 This adds an output label of "ui.warning".
631 This adds an output label of "ui.warning".
623 '''
632 '''
624 opts['label'] = opts.get('label', '') + ' ui.warning'
633 opts['label'] = opts.get('label', '') + ' ui.warning'
625 self.write_err(*msg, **opts)
634 self.write_err(*msg, **opts)
626 def note(self, *msg, **opts):
635 def note(self, *msg, **opts):
627 '''write note to output (if ui.verbose is True)
636 '''write note to output (if ui.verbose is True)
628
637
629 This adds an output label of "ui.note".
638 This adds an output label of "ui.note".
630 '''
639 '''
631 if self.verbose:
640 if self.verbose:
632 opts['label'] = opts.get('label', '') + ' ui.note'
641 opts['label'] = opts.get('label', '') + ' ui.note'
633 self.write(*msg, **opts)
642 self.write(*msg, **opts)
634 def debug(self, *msg, **opts):
643 def debug(self, *msg, **opts):
635 '''write debug message to output (if ui.debugflag is True)
644 '''write debug message to output (if ui.debugflag is True)
636
645
637 This adds an output label of "ui.debug".
646 This adds an output label of "ui.debug".
638 '''
647 '''
639 if self.debugflag:
648 if self.debugflag:
640 opts['label'] = opts.get('label', '') + ' ui.debug'
649 opts['label'] = opts.get('label', '') + ' ui.debug'
641 self.write(*msg, **opts)
650 self.write(*msg, **opts)
642 def edit(self, text, user):
651 def edit(self, text, user):
643 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
652 (fd, name) = tempfile.mkstemp(prefix="hg-editor-", suffix=".txt",
644 text=True)
653 text=True)
645 try:
654 try:
646 f = os.fdopen(fd, "w")
655 f = os.fdopen(fd, "w")
647 f.write(text)
656 f.write(text)
648 f.close()
657 f.close()
649
658
650 editor = self.geteditor()
659 editor = self.geteditor()
651
660
652 util.system("%s \"%s\"" % (editor, name),
661 util.system("%s \"%s\"" % (editor, name),
653 environ={'HGUSER': user},
662 environ={'HGUSER': user},
654 onerr=util.Abort, errprefix=_("edit failed"),
663 onerr=util.Abort, errprefix=_("edit failed"),
655 out=self.fout)
664 out=self.fout)
656
665
657 f = open(name)
666 f = open(name)
658 t = f.read()
667 t = f.read()
659 f.close()
668 f.close()
660 finally:
669 finally:
661 os.unlink(name)
670 os.unlink(name)
662
671
663 return t
672 return t
664
673
665 def traceback(self, exc=None):
674 def traceback(self, exc=None):
666 '''print exception traceback if traceback printing enabled.
675 '''print exception traceback if traceback printing enabled.
667 only to call in exception handler. returns true if traceback
676 only to call in exception handler. returns true if traceback
668 printed.'''
677 printed.'''
669 if self.tracebackflag:
678 if self.tracebackflag:
670 if exc:
679 if exc:
671 traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
680 traceback.print_exception(exc[0], exc[1], exc[2], file=self.ferr)
672 else:
681 else:
673 traceback.print_exc(file=self.ferr)
682 traceback.print_exc(file=self.ferr)
674 return self.tracebackflag
683 return self.tracebackflag
675
684
676 def geteditor(self):
685 def geteditor(self):
677 '''return editor to use'''
686 '''return editor to use'''
678 return (os.environ.get("HGEDITOR") or
687 return (os.environ.get("HGEDITOR") or
679 self.config("ui", "editor") or
688 self.config("ui", "editor") or
680 os.environ.get("VISUAL") or
689 os.environ.get("VISUAL") or
681 os.environ.get("EDITOR", "vi"))
690 os.environ.get("EDITOR", "vi"))
682
691
683 def progress(self, topic, pos, item="", unit="", total=None):
692 def progress(self, topic, pos, item="", unit="", total=None):
684 '''show a progress message
693 '''show a progress message
685
694
686 With stock hg, this is simply a debug message that is hidden
695 With stock hg, this is simply a debug message that is hidden
687 by default, but with extensions or GUI tools it may be
696 by default, but with extensions or GUI tools it may be
688 visible. 'topic' is the current operation, 'item' is a
697 visible. 'topic' is the current operation, 'item' is a
689 non-numeric marker of the current position (ie the currently
698 non-numeric marker of the current position (ie the currently
690 in-process file), 'pos' is the current numeric position (ie
699 in-process file), 'pos' is the current numeric position (ie
691 revision, bytes, etc.), unit is a corresponding unit label,
700 revision, bytes, etc.), unit is a corresponding unit label,
692 and total is the highest expected pos.
701 and total is the highest expected pos.
693
702
694 Multiple nested topics may be active at a time.
703 Multiple nested topics may be active at a time.
695
704
696 All topics should be marked closed by setting pos to None at
705 All topics should be marked closed by setting pos to None at
697 termination.
706 termination.
698 '''
707 '''
699
708
700 if pos is None or not self.debugflag:
709 if pos is None or not self.debugflag:
701 return
710 return
702
711
703 if unit:
712 if unit:
704 unit = ' ' + unit
713 unit = ' ' + unit
705 if item:
714 if item:
706 item = ' ' + item
715 item = ' ' + item
707
716
708 if total:
717 if total:
709 pct = 100.0 * pos / total
718 pct = 100.0 * pos / total
710 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
719 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
711 % (topic, item, pos, total, unit, pct))
720 % (topic, item, pos, total, unit, pct))
712 else:
721 else:
713 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
722 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
714
723
715 def log(self, service, message):
724 def log(self, service, message):
716 '''hook for logging facility extensions
725 '''hook for logging facility extensions
717
726
718 service should be a readily-identifiable subsystem, which will
727 service should be a readily-identifiable subsystem, which will
719 allow filtering.
728 allow filtering.
720 message should be a newline-terminated string to log.
729 message should be a newline-terminated string to log.
721 '''
730 '''
722 pass
731 pass
723
732
724 def label(self, msg, label):
733 def label(self, msg, label):
725 '''style msg based on supplied label
734 '''style msg based on supplied label
726
735
727 Like ui.write(), this just returns msg unchanged, but extensions
736 Like ui.write(), this just returns msg unchanged, but extensions
728 and GUI tools can override it to allow styling output without
737 and GUI tools can override it to allow styling output without
729 writing it.
738 writing it.
730
739
731 ui.write(s, 'label') is equivalent to
740 ui.write(s, 'label') is equivalent to
732 ui.write(ui.label(s, 'label')).
741 ui.write(ui.label(s, 'label')).
733 '''
742 '''
734 return msg
743 return msg
General Comments 0
You need to be logged in to leave comments. Login now