##// END OF EJS Templates
config: add hasconfig method and supporting plumbing...
Bryan O'Sullivan -
r27696:e70c97cc default
parent child Browse files
Show More
@@ -1,171 +1,173 b''
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import os
11 import os
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 util,
16 util,
17 )
17 )
18
18
19 class config(object):
19 class config(object):
20 def __init__(self, data=None, includepaths=[]):
20 def __init__(self, data=None, includepaths=[]):
21 self._data = {}
21 self._data = {}
22 self._source = {}
22 self._source = {}
23 self._unset = []
23 self._unset = []
24 self._includepaths = includepaths
24 self._includepaths = includepaths
25 if data:
25 if data:
26 for k in data._data:
26 for k in data._data:
27 self._data[k] = data[k].copy()
27 self._data[k] = data[k].copy()
28 self._source = data._source.copy()
28 self._source = data._source.copy()
29 def copy(self):
29 def copy(self):
30 return config(self)
30 return config(self)
31 def __contains__(self, section):
31 def __contains__(self, section):
32 return section in self._data
32 return section in self._data
33 def hasitem(self, section, item):
34 return item in self._data.get(section, {})
33 def __getitem__(self, section):
35 def __getitem__(self, section):
34 return self._data.get(section, {})
36 return self._data.get(section, {})
35 def __iter__(self):
37 def __iter__(self):
36 for d in self.sections():
38 for d in self.sections():
37 yield d
39 yield d
38 def update(self, src):
40 def update(self, src):
39 for s, n in src._unset:
41 for s, n in src._unset:
40 if s in self and n in self._data[s]:
42 if s in self and n in self._data[s]:
41 del self._data[s][n]
43 del self._data[s][n]
42 del self._source[(s, n)]
44 del self._source[(s, n)]
43 for s in src:
45 for s in src:
44 if s not in self:
46 if s not in self:
45 self._data[s] = util.sortdict()
47 self._data[s] = util.sortdict()
46 self._data[s].update(src._data[s])
48 self._data[s].update(src._data[s])
47 self._source.update(src._source)
49 self._source.update(src._source)
48 def get(self, section, item, default=None):
50 def get(self, section, item, default=None):
49 return self._data.get(section, {}).get(item, default)
51 return self._data.get(section, {}).get(item, default)
50
52
51 def backup(self, section, item):
53 def backup(self, section, item):
52 """return a tuple allowing restore to reinstall a previous value
54 """return a tuple allowing restore to reinstall a previous value
53
55
54 The main reason we need it is because it handles the "no data" case.
56 The main reason we need it is because it handles the "no data" case.
55 """
57 """
56 try:
58 try:
57 value = self._data[section][item]
59 value = self._data[section][item]
58 source = self.source(section, item)
60 source = self.source(section, item)
59 return (section, item, value, source)
61 return (section, item, value, source)
60 except KeyError:
62 except KeyError:
61 return (section, item)
63 return (section, item)
62
64
63 def source(self, section, item):
65 def source(self, section, item):
64 return self._source.get((section, item), "")
66 return self._source.get((section, item), "")
65 def sections(self):
67 def sections(self):
66 return sorted(self._data.keys())
68 return sorted(self._data.keys())
67 def items(self, section):
69 def items(self, section):
68 return self._data.get(section, {}).items()
70 return self._data.get(section, {}).items()
69 def set(self, section, item, value, source=""):
71 def set(self, section, item, value, source=""):
70 if section not in self:
72 if section not in self:
71 self._data[section] = util.sortdict()
73 self._data[section] = util.sortdict()
72 self._data[section][item] = value
74 self._data[section][item] = value
73 if source:
75 if source:
74 self._source[(section, item)] = source
76 self._source[(section, item)] = source
75
77
76 def restore(self, data):
78 def restore(self, data):
77 """restore data returned by self.backup"""
79 """restore data returned by self.backup"""
78 if len(data) == 4:
80 if len(data) == 4:
79 # restore old data
81 # restore old data
80 section, item, value, source = data
82 section, item, value, source = data
81 self._data[section][item] = value
83 self._data[section][item] = value
82 self._source[(section, item)] = source
84 self._source[(section, item)] = source
83 else:
85 else:
84 # no data before, remove everything
86 # no data before, remove everything
85 section, item = data
87 section, item = data
86 if section in self._data:
88 if section in self._data:
87 self._data[section].pop(item, None)
89 self._data[section].pop(item, None)
88 self._source.pop((section, item), None)
90 self._source.pop((section, item), None)
89
91
90 def parse(self, src, data, sections=None, remap=None, include=None):
92 def parse(self, src, data, sections=None, remap=None, include=None):
91 sectionre = util.re.compile(r'\[([^\[]+)\]')
93 sectionre = util.re.compile(r'\[([^\[]+)\]')
92 itemre = util.re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
94 itemre = util.re.compile(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
93 contre = util.re.compile(r'\s+(\S|\S.*\S)\s*$')
95 contre = util.re.compile(r'\s+(\S|\S.*\S)\s*$')
94 emptyre = util.re.compile(r'(;|#|\s*$)')
96 emptyre = util.re.compile(r'(;|#|\s*$)')
95 commentre = util.re.compile(r'(;|#)')
97 commentre = util.re.compile(r'(;|#)')
96 unsetre = util.re.compile(r'%unset\s+(\S+)')
98 unsetre = util.re.compile(r'%unset\s+(\S+)')
97 includere = util.re.compile(r'%include\s+(\S|\S.*\S)\s*$')
99 includere = util.re.compile(r'%include\s+(\S|\S.*\S)\s*$')
98 section = ""
100 section = ""
99 item = None
101 item = None
100 line = 0
102 line = 0
101 cont = False
103 cont = False
102
104
103 for l in data.splitlines(True):
105 for l in data.splitlines(True):
104 line += 1
106 line += 1
105 if line == 1 and l.startswith('\xef\xbb\xbf'):
107 if line == 1 and l.startswith('\xef\xbb\xbf'):
106 # Someone set us up the BOM
108 # Someone set us up the BOM
107 l = l[3:]
109 l = l[3:]
108 if cont:
110 if cont:
109 if commentre.match(l):
111 if commentre.match(l):
110 continue
112 continue
111 m = contre.match(l)
113 m = contre.match(l)
112 if m:
114 if m:
113 if sections and section not in sections:
115 if sections and section not in sections:
114 continue
116 continue
115 v = self.get(section, item) + "\n" + m.group(1)
117 v = self.get(section, item) + "\n" + m.group(1)
116 self.set(section, item, v, "%s:%d" % (src, line))
118 self.set(section, item, v, "%s:%d" % (src, line))
117 continue
119 continue
118 item = None
120 item = None
119 cont = False
121 cont = False
120 m = includere.match(l)
122 m = includere.match(l)
121
123
122 if m and include:
124 if m and include:
123 expanded = util.expandpath(m.group(1))
125 expanded = util.expandpath(m.group(1))
124 includepaths = [os.path.dirname(src)] + self._includepaths
126 includepaths = [os.path.dirname(src)] + self._includepaths
125
127
126 for base in includepaths:
128 for base in includepaths:
127 inc = os.path.normpath(os.path.join(base, expanded))
129 inc = os.path.normpath(os.path.join(base, expanded))
128
130
129 try:
131 try:
130 include(inc, remap=remap, sections=sections)
132 include(inc, remap=remap, sections=sections)
131 break
133 break
132 except IOError as inst:
134 except IOError as inst:
133 if inst.errno != errno.ENOENT:
135 if inst.errno != errno.ENOENT:
134 raise error.ParseError(_("cannot include %s (%s)")
136 raise error.ParseError(_("cannot include %s (%s)")
135 % (inc, inst.strerror),
137 % (inc, inst.strerror),
136 "%s:%s" % (src, line))
138 "%s:%s" % (src, line))
137 continue
139 continue
138 if emptyre.match(l):
140 if emptyre.match(l):
139 continue
141 continue
140 m = sectionre.match(l)
142 m = sectionre.match(l)
141 if m:
143 if m:
142 section = m.group(1)
144 section = m.group(1)
143 if remap:
145 if remap:
144 section = remap.get(section, section)
146 section = remap.get(section, section)
145 if section not in self:
147 if section not in self:
146 self._data[section] = util.sortdict()
148 self._data[section] = util.sortdict()
147 continue
149 continue
148 m = itemre.match(l)
150 m = itemre.match(l)
149 if m:
151 if m:
150 item = m.group(1)
152 item = m.group(1)
151 cont = True
153 cont = True
152 if sections and section not in sections:
154 if sections and section not in sections:
153 continue
155 continue
154 self.set(section, item, m.group(2), "%s:%d" % (src, line))
156 self.set(section, item, m.group(2), "%s:%d" % (src, line))
155 continue
157 continue
156 m = unsetre.match(l)
158 m = unsetre.match(l)
157 if m:
159 if m:
158 name = m.group(1)
160 name = m.group(1)
159 if sections and section not in sections:
161 if sections and section not in sections:
160 continue
162 continue
161 if self.get(section, name) is not None:
163 if self.get(section, name) is not None:
162 del self._data[section][name]
164 del self._data[section][name]
163 self._unset.append((section, name))
165 self._unset.append((section, name))
164 continue
166 continue
165
167
166 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
168 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
167
169
168 def read(self, path, fp=None, sections=None, remap=None):
170 def read(self, path, fp=None, sections=None, remap=None):
169 if not fp:
171 if not fp:
170 fp = util.posixfile(path)
172 fp = util.posixfile(path)
171 self.parse(path, fp.read(), sections, remap, self.read)
173 self.parse(path, fp.read(), sections, remap, self.read)
@@ -1,1260 +1,1263 b''
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 __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import getpass
11 import getpass
12 import inspect
12 import inspect
13 import os
13 import os
14 import re
14 import re
15 import socket
15 import socket
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18 import traceback
18 import traceback
19
19
20 from .i18n import _
20 from .i18n import _
21 from .node import hex
21 from .node import hex
22
22
23 from . import (
23 from . import (
24 config,
24 config,
25 error,
25 error,
26 formatter,
26 formatter,
27 progress,
27 progress,
28 scmutil,
28 scmutil,
29 util,
29 util,
30 )
30 )
31
31
32 samplehgrcs = {
32 samplehgrcs = {
33 'user':
33 'user':
34 """# example user config (see "hg help config" for more info)
34 """# example user config (see "hg help config" for more info)
35 [ui]
35 [ui]
36 # name and email, e.g.
36 # name and email, e.g.
37 # username = Jane Doe <jdoe@example.com>
37 # username = Jane Doe <jdoe@example.com>
38 username =
38 username =
39
39
40 [extensions]
40 [extensions]
41 # uncomment these lines to enable some popular extensions
41 # uncomment these lines to enable some popular extensions
42 # (see "hg help extensions" for more info)
42 # (see "hg help extensions" for more info)
43 #
43 #
44 # pager =
44 # pager =
45 # progress =
45 # progress =
46 # color =""",
46 # color =""",
47
47
48 'cloned':
48 'cloned':
49 """# example repository config (see "hg help config" for more info)
49 """# example repository config (see "hg help config" for more info)
50 [paths]
50 [paths]
51 default = %s
51 default = %s
52
52
53 # path aliases to other clones of this repo in URLs or filesystem paths
53 # path aliases to other clones of this repo in URLs or filesystem paths
54 # (see "hg help config.paths" for more info)
54 # (see "hg help config.paths" for more info)
55 #
55 #
56 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
56 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
57 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
57 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
58 # my-clone = /home/jdoe/jdoes-clone
58 # my-clone = /home/jdoe/jdoes-clone
59
59
60 [ui]
60 [ui]
61 # name and email (local to this repository, optional), e.g.
61 # name and email (local to this repository, optional), e.g.
62 # username = Jane Doe <jdoe@example.com>
62 # username = Jane Doe <jdoe@example.com>
63 """,
63 """,
64
64
65 'local':
65 'local':
66 """# example repository config (see "hg help config" for more info)
66 """# example repository config (see "hg help config" for more info)
67 [paths]
67 [paths]
68 # path aliases to other clones of this repo in URLs or filesystem paths
68 # path aliases to other clones of this repo in URLs or filesystem paths
69 # (see "hg help config.paths" for more info)
69 # (see "hg help config.paths" for more info)
70 #
70 #
71 # default = http://example.com/hg/example-repo
71 # default = http://example.com/hg/example-repo
72 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
72 # default-push = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
73 # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
74 # my-clone = /home/jdoe/jdoes-clone
74 # my-clone = /home/jdoe/jdoes-clone
75
75
76 [ui]
76 [ui]
77 # name and email (local to this repository, optional), e.g.
77 # name and email (local to this repository, optional), e.g.
78 # username = Jane Doe <jdoe@example.com>
78 # username = Jane Doe <jdoe@example.com>
79 """,
79 """,
80
80
81 'global':
81 'global':
82 """# example system-wide hg config (see "hg help config" for more info)
82 """# example system-wide hg config (see "hg help config" for more info)
83
83
84 [extensions]
84 [extensions]
85 # uncomment these lines to enable some popular extensions
85 # uncomment these lines to enable some popular extensions
86 # (see "hg help extensions" for more info)
86 # (see "hg help extensions" for more info)
87 #
87 #
88 # blackbox =
88 # blackbox =
89 # progress =
89 # progress =
90 # color =
90 # color =
91 # pager =""",
91 # pager =""",
92 }
92 }
93
93
94 class ui(object):
94 class ui(object):
95 def __init__(self, src=None):
95 def __init__(self, src=None):
96 # _buffers: used for temporary capture of output
96 # _buffers: used for temporary capture of output
97 self._buffers = []
97 self._buffers = []
98 # 3-tuple describing how each buffer in the stack behaves.
98 # 3-tuple describing how each buffer in the stack behaves.
99 # Values are (capture stderr, capture subprocesses, apply labels).
99 # Values are (capture stderr, capture subprocesses, apply labels).
100 self._bufferstates = []
100 self._bufferstates = []
101 # When a buffer is active, defines whether we are expanding labels.
101 # When a buffer is active, defines whether we are expanding labels.
102 # This exists to prevent an extra list lookup.
102 # This exists to prevent an extra list lookup.
103 self._bufferapplylabels = None
103 self._bufferapplylabels = None
104 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
104 self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
105 self._reportuntrusted = True
105 self._reportuntrusted = True
106 self._ocfg = config.config() # overlay
106 self._ocfg = config.config() # overlay
107 self._tcfg = config.config() # trusted
107 self._tcfg = config.config() # trusted
108 self._ucfg = config.config() # untrusted
108 self._ucfg = config.config() # untrusted
109 self._trustusers = set()
109 self._trustusers = set()
110 self._trustgroups = set()
110 self._trustgroups = set()
111 self.callhooks = True
111 self.callhooks = True
112
112
113 if src:
113 if src:
114 self.fout = src.fout
114 self.fout = src.fout
115 self.ferr = src.ferr
115 self.ferr = src.ferr
116 self.fin = src.fin
116 self.fin = src.fin
117
117
118 self._tcfg = src._tcfg.copy()
118 self._tcfg = src._tcfg.copy()
119 self._ucfg = src._ucfg.copy()
119 self._ucfg = src._ucfg.copy()
120 self._ocfg = src._ocfg.copy()
120 self._ocfg = src._ocfg.copy()
121 self._trustusers = src._trustusers.copy()
121 self._trustusers = src._trustusers.copy()
122 self._trustgroups = src._trustgroups.copy()
122 self._trustgroups = src._trustgroups.copy()
123 self.environ = src.environ
123 self.environ = src.environ
124 self.callhooks = src.callhooks
124 self.callhooks = src.callhooks
125 self.fixconfig()
125 self.fixconfig()
126 else:
126 else:
127 self.fout = sys.stdout
127 self.fout = sys.stdout
128 self.ferr = sys.stderr
128 self.ferr = sys.stderr
129 self.fin = sys.stdin
129 self.fin = sys.stdin
130
130
131 # shared read-only environment
131 # shared read-only environment
132 self.environ = os.environ
132 self.environ = os.environ
133 # we always trust global config files
133 # we always trust global config files
134 for f in scmutil.rcpath():
134 for f in scmutil.rcpath():
135 self.readconfig(f, trust=True)
135 self.readconfig(f, trust=True)
136
136
137 def copy(self):
137 def copy(self):
138 return self.__class__(self)
138 return self.__class__(self)
139
139
140 def formatter(self, topic, opts):
140 def formatter(self, topic, opts):
141 return formatter.formatter(self, topic, opts)
141 return formatter.formatter(self, topic, opts)
142
142
143 def _trusted(self, fp, f):
143 def _trusted(self, fp, f):
144 st = util.fstat(fp)
144 st = util.fstat(fp)
145 if util.isowner(st):
145 if util.isowner(st):
146 return True
146 return True
147
147
148 tusers, tgroups = self._trustusers, self._trustgroups
148 tusers, tgroups = self._trustusers, self._trustgroups
149 if '*' in tusers or '*' in tgroups:
149 if '*' in tusers or '*' in tgroups:
150 return True
150 return True
151
151
152 user = util.username(st.st_uid)
152 user = util.username(st.st_uid)
153 group = util.groupname(st.st_gid)
153 group = util.groupname(st.st_gid)
154 if user in tusers or group in tgroups or user == util.username():
154 if user in tusers or group in tgroups or user == util.username():
155 return True
155 return True
156
156
157 if self._reportuntrusted:
157 if self._reportuntrusted:
158 self.warn(_('not trusting file %s from untrusted '
158 self.warn(_('not trusting file %s from untrusted '
159 'user %s, group %s\n') % (f, user, group))
159 'user %s, group %s\n') % (f, user, group))
160 return False
160 return False
161
161
162 def readconfig(self, filename, root=None, trust=False,
162 def readconfig(self, filename, root=None, trust=False,
163 sections=None, remap=None):
163 sections=None, remap=None):
164 try:
164 try:
165 fp = open(filename)
165 fp = open(filename)
166 except IOError:
166 except IOError:
167 if not sections: # ignore unless we were looking for something
167 if not sections: # ignore unless we were looking for something
168 return
168 return
169 raise
169 raise
170
170
171 cfg = config.config()
171 cfg = config.config()
172 trusted = sections or trust or self._trusted(fp, filename)
172 trusted = sections or trust or self._trusted(fp, filename)
173
173
174 try:
174 try:
175 cfg.read(filename, fp, sections=sections, remap=remap)
175 cfg.read(filename, fp, sections=sections, remap=remap)
176 fp.close()
176 fp.close()
177 except error.ConfigError as inst:
177 except error.ConfigError as inst:
178 if trusted:
178 if trusted:
179 raise
179 raise
180 self.warn(_("ignored: %s\n") % str(inst))
180 self.warn(_("ignored: %s\n") % str(inst))
181
181
182 if self.plain():
182 if self.plain():
183 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
183 for k in ('debug', 'fallbackencoding', 'quiet', 'slash',
184 'logtemplate', 'statuscopies', 'style',
184 'logtemplate', 'statuscopies', 'style',
185 'traceback', 'verbose'):
185 'traceback', 'verbose'):
186 if k in cfg['ui']:
186 if k in cfg['ui']:
187 del cfg['ui'][k]
187 del cfg['ui'][k]
188 for k, v in cfg.items('defaults'):
188 for k, v in cfg.items('defaults'):
189 del cfg['defaults'][k]
189 del cfg['defaults'][k]
190 # Don't remove aliases from the configuration if in the exceptionlist
190 # Don't remove aliases from the configuration if in the exceptionlist
191 if self.plain('alias'):
191 if self.plain('alias'):
192 for k, v in cfg.items('alias'):
192 for k, v in cfg.items('alias'):
193 del cfg['alias'][k]
193 del cfg['alias'][k]
194 if self.plain('revsetalias'):
194 if self.plain('revsetalias'):
195 for k, v in cfg.items('revsetalias'):
195 for k, v in cfg.items('revsetalias'):
196 del cfg['revsetalias'][k]
196 del cfg['revsetalias'][k]
197
197
198 if trusted:
198 if trusted:
199 self._tcfg.update(cfg)
199 self._tcfg.update(cfg)
200 self._tcfg.update(self._ocfg)
200 self._tcfg.update(self._ocfg)
201 self._ucfg.update(cfg)
201 self._ucfg.update(cfg)
202 self._ucfg.update(self._ocfg)
202 self._ucfg.update(self._ocfg)
203
203
204 if root is None:
204 if root is None:
205 root = os.path.expanduser('~')
205 root = os.path.expanduser('~')
206 self.fixconfig(root=root)
206 self.fixconfig(root=root)
207
207
208 def fixconfig(self, root=None, section=None):
208 def fixconfig(self, root=None, section=None):
209 if section in (None, 'paths'):
209 if section in (None, 'paths'):
210 # expand vars and ~
210 # expand vars and ~
211 # translate paths relative to root (or home) into absolute paths
211 # translate paths relative to root (or home) into absolute paths
212 root = root or os.getcwd()
212 root = root or os.getcwd()
213 for c in self._tcfg, self._ucfg, self._ocfg:
213 for c in self._tcfg, self._ucfg, self._ocfg:
214 for n, p in c.items('paths'):
214 for n, p in c.items('paths'):
215 if not p:
215 if not p:
216 continue
216 continue
217 if '%%' in p:
217 if '%%' in p:
218 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
218 self.warn(_("(deprecated '%%' in path %s=%s from %s)\n")
219 % (n, p, self.configsource('paths', n)))
219 % (n, p, self.configsource('paths', n)))
220 p = p.replace('%%', '%')
220 p = p.replace('%%', '%')
221 p = util.expandpath(p)
221 p = util.expandpath(p)
222 if not util.hasscheme(p) and not os.path.isabs(p):
222 if not util.hasscheme(p) and not os.path.isabs(p):
223 p = os.path.normpath(os.path.join(root, p))
223 p = os.path.normpath(os.path.join(root, p))
224 c.set("paths", n, p)
224 c.set("paths", n, p)
225
225
226 if section in (None, 'ui'):
226 if section in (None, 'ui'):
227 # update ui options
227 # update ui options
228 self.debugflag = self.configbool('ui', 'debug')
228 self.debugflag = self.configbool('ui', 'debug')
229 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
229 self.verbose = self.debugflag or self.configbool('ui', 'verbose')
230 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
230 self.quiet = not self.debugflag and self.configbool('ui', 'quiet')
231 if self.verbose and self.quiet:
231 if self.verbose and self.quiet:
232 self.quiet = self.verbose = False
232 self.quiet = self.verbose = False
233 self._reportuntrusted = self.debugflag or self.configbool("ui",
233 self._reportuntrusted = self.debugflag or self.configbool("ui",
234 "report_untrusted", True)
234 "report_untrusted", True)
235 self.tracebackflag = self.configbool('ui', 'traceback', False)
235 self.tracebackflag = self.configbool('ui', 'traceback', False)
236
236
237 if section in (None, 'trusted'):
237 if section in (None, 'trusted'):
238 # update trust information
238 # update trust information
239 self._trustusers.update(self.configlist('trusted', 'users'))
239 self._trustusers.update(self.configlist('trusted', 'users'))
240 self._trustgroups.update(self.configlist('trusted', 'groups'))
240 self._trustgroups.update(self.configlist('trusted', 'groups'))
241
241
242 def backupconfig(self, section, item):
242 def backupconfig(self, section, item):
243 return (self._ocfg.backup(section, item),
243 return (self._ocfg.backup(section, item),
244 self._tcfg.backup(section, item),
244 self._tcfg.backup(section, item),
245 self._ucfg.backup(section, item),)
245 self._ucfg.backup(section, item),)
246 def restoreconfig(self, data):
246 def restoreconfig(self, data):
247 self._ocfg.restore(data[0])
247 self._ocfg.restore(data[0])
248 self._tcfg.restore(data[1])
248 self._tcfg.restore(data[1])
249 self._ucfg.restore(data[2])
249 self._ucfg.restore(data[2])
250
250
251 def setconfig(self, section, name, value, source=''):
251 def setconfig(self, section, name, value, source=''):
252 for cfg in (self._ocfg, self._tcfg, self._ucfg):
252 for cfg in (self._ocfg, self._tcfg, self._ucfg):
253 cfg.set(section, name, value, source)
253 cfg.set(section, name, value, source)
254 self.fixconfig(section=section)
254 self.fixconfig(section=section)
255
255
256 def _data(self, untrusted):
256 def _data(self, untrusted):
257 return untrusted and self._ucfg or self._tcfg
257 return untrusted and self._ucfg or self._tcfg
258
258
259 def configsource(self, section, name, untrusted=False):
259 def configsource(self, section, name, untrusted=False):
260 return self._data(untrusted).source(section, name) or 'none'
260 return self._data(untrusted).source(section, name) or 'none'
261
261
262 def config(self, section, name, default=None, untrusted=False):
262 def config(self, section, name, default=None, untrusted=False):
263 if isinstance(name, list):
263 if isinstance(name, list):
264 alternates = name
264 alternates = name
265 else:
265 else:
266 alternates = [name]
266 alternates = [name]
267
267
268 for n in alternates:
268 for n in alternates:
269 value = self._data(untrusted).get(section, n, None)
269 value = self._data(untrusted).get(section, n, None)
270 if value is not None:
270 if value is not None:
271 name = n
271 name = n
272 break
272 break
273 else:
273 else:
274 value = default
274 value = default
275
275
276 if self.debugflag and not untrusted and self._reportuntrusted:
276 if self.debugflag and not untrusted and self._reportuntrusted:
277 for n in alternates:
277 for n in alternates:
278 uvalue = self._ucfg.get(section, n)
278 uvalue = self._ucfg.get(section, n)
279 if uvalue is not None and uvalue != value:
279 if uvalue is not None and uvalue != value:
280 self.debug("ignoring untrusted configuration option "
280 self.debug("ignoring untrusted configuration option "
281 "%s.%s = %s\n" % (section, n, uvalue))
281 "%s.%s = %s\n" % (section, n, uvalue))
282 return value
282 return value
283
283
284 def configsuboptions(self, section, name, default=None, untrusted=False):
284 def configsuboptions(self, section, name, default=None, untrusted=False):
285 """Get a config option and all sub-options.
285 """Get a config option and all sub-options.
286
286
287 Some config options have sub-options that are declared with the
287 Some config options have sub-options that are declared with the
288 format "key:opt = value". This method is used to return the main
288 format "key:opt = value". This method is used to return the main
289 option and all its declared sub-options.
289 option and all its declared sub-options.
290
290
291 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
291 Returns a 2-tuple of ``(option, sub-options)``, where `sub-options``
292 is a dict of defined sub-options where keys and values are strings.
292 is a dict of defined sub-options where keys and values are strings.
293 """
293 """
294 data = self._data(untrusted)
294 data = self._data(untrusted)
295 main = data.get(section, name, default)
295 main = data.get(section, name, default)
296 if self.debugflag and not untrusted and self._reportuntrusted:
296 if self.debugflag and not untrusted and self._reportuntrusted:
297 uvalue = self._ucfg.get(section, name)
297 uvalue = self._ucfg.get(section, name)
298 if uvalue is not None and uvalue != main:
298 if uvalue is not None and uvalue != main:
299 self.debug('ignoring untrusted configuration option '
299 self.debug('ignoring untrusted configuration option '
300 '%s.%s = %s\n' % (section, name, uvalue))
300 '%s.%s = %s\n' % (section, name, uvalue))
301
301
302 sub = {}
302 sub = {}
303 prefix = '%s:' % name
303 prefix = '%s:' % name
304 for k, v in data.items(section):
304 for k, v in data.items(section):
305 if k.startswith(prefix):
305 if k.startswith(prefix):
306 sub[k[len(prefix):]] = v
306 sub[k[len(prefix):]] = v
307
307
308 if self.debugflag and not untrusted and self._reportuntrusted:
308 if self.debugflag and not untrusted and self._reportuntrusted:
309 for k, v in sub.items():
309 for k, v in sub.items():
310 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
310 uvalue = self._ucfg.get(section, '%s:%s' % (name, k))
311 if uvalue is not None and uvalue != v:
311 if uvalue is not None and uvalue != v:
312 self.debug('ignoring untrusted configuration option '
312 self.debug('ignoring untrusted configuration option '
313 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
313 '%s:%s.%s = %s\n' % (section, name, k, uvalue))
314
314
315 return main, sub
315 return main, sub
316
316
317 def configpath(self, section, name, default=None, untrusted=False):
317 def configpath(self, section, name, default=None, untrusted=False):
318 'get a path config item, expanded relative to repo root or config file'
318 'get a path config item, expanded relative to repo root or config file'
319 v = self.config(section, name, default, untrusted)
319 v = self.config(section, name, default, untrusted)
320 if v is None:
320 if v is None:
321 return None
321 return None
322 if not os.path.isabs(v) or "://" not in v:
322 if not os.path.isabs(v) or "://" not in v:
323 src = self.configsource(section, name, untrusted)
323 src = self.configsource(section, name, untrusted)
324 if ':' in src:
324 if ':' in src:
325 base = os.path.dirname(src.rsplit(':')[0])
325 base = os.path.dirname(src.rsplit(':')[0])
326 v = os.path.join(base, os.path.expanduser(v))
326 v = os.path.join(base, os.path.expanduser(v))
327 return v
327 return v
328
328
329 def configbool(self, section, name, default=False, untrusted=False):
329 def configbool(self, section, name, default=False, untrusted=False):
330 """parse a configuration element as a boolean
330 """parse a configuration element as a boolean
331
331
332 >>> u = ui(); s = 'foo'
332 >>> u = ui(); s = 'foo'
333 >>> u.setconfig(s, 'true', 'yes')
333 >>> u.setconfig(s, 'true', 'yes')
334 >>> u.configbool(s, 'true')
334 >>> u.configbool(s, 'true')
335 True
335 True
336 >>> u.setconfig(s, 'false', 'no')
336 >>> u.setconfig(s, 'false', 'no')
337 >>> u.configbool(s, 'false')
337 >>> u.configbool(s, 'false')
338 False
338 False
339 >>> u.configbool(s, 'unknown')
339 >>> u.configbool(s, 'unknown')
340 False
340 False
341 >>> u.configbool(s, 'unknown', True)
341 >>> u.configbool(s, 'unknown', True)
342 True
342 True
343 >>> u.setconfig(s, 'invalid', 'somevalue')
343 >>> u.setconfig(s, 'invalid', 'somevalue')
344 >>> u.configbool(s, 'invalid')
344 >>> u.configbool(s, 'invalid')
345 Traceback (most recent call last):
345 Traceback (most recent call last):
346 ...
346 ...
347 ConfigError: foo.invalid is not a boolean ('somevalue')
347 ConfigError: foo.invalid is not a boolean ('somevalue')
348 """
348 """
349
349
350 v = self.config(section, name, None, untrusted)
350 v = self.config(section, name, None, untrusted)
351 if v is None:
351 if v is None:
352 return default
352 return default
353 if isinstance(v, bool):
353 if isinstance(v, bool):
354 return v
354 return v
355 b = util.parsebool(v)
355 b = util.parsebool(v)
356 if b is None:
356 if b is None:
357 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
357 raise error.ConfigError(_("%s.%s is not a boolean ('%s')")
358 % (section, name, v))
358 % (section, name, v))
359 return b
359 return b
360
360
361 def configint(self, section, name, default=None, untrusted=False):
361 def configint(self, section, name, default=None, untrusted=False):
362 """parse a configuration element as an integer
362 """parse a configuration element as an integer
363
363
364 >>> u = ui(); s = 'foo'
364 >>> u = ui(); s = 'foo'
365 >>> u.setconfig(s, 'int1', '42')
365 >>> u.setconfig(s, 'int1', '42')
366 >>> u.configint(s, 'int1')
366 >>> u.configint(s, 'int1')
367 42
367 42
368 >>> u.setconfig(s, 'int2', '-42')
368 >>> u.setconfig(s, 'int2', '-42')
369 >>> u.configint(s, 'int2')
369 >>> u.configint(s, 'int2')
370 -42
370 -42
371 >>> u.configint(s, 'unknown', 7)
371 >>> u.configint(s, 'unknown', 7)
372 7
372 7
373 >>> u.setconfig(s, 'invalid', 'somevalue')
373 >>> u.setconfig(s, 'invalid', 'somevalue')
374 >>> u.configint(s, 'invalid')
374 >>> u.configint(s, 'invalid')
375 Traceback (most recent call last):
375 Traceback (most recent call last):
376 ...
376 ...
377 ConfigError: foo.invalid is not an integer ('somevalue')
377 ConfigError: foo.invalid is not an integer ('somevalue')
378 """
378 """
379
379
380 v = self.config(section, name, None, untrusted)
380 v = self.config(section, name, None, untrusted)
381 if v is None:
381 if v is None:
382 return default
382 return default
383 try:
383 try:
384 return int(v)
384 return int(v)
385 except ValueError:
385 except ValueError:
386 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
386 raise error.ConfigError(_("%s.%s is not an integer ('%s')")
387 % (section, name, v))
387 % (section, name, v))
388
388
389 def configbytes(self, section, name, default=0, untrusted=False):
389 def configbytes(self, section, name, default=0, untrusted=False):
390 """parse a configuration element as a quantity in bytes
390 """parse a configuration element as a quantity in bytes
391
391
392 Units can be specified as b (bytes), k or kb (kilobytes), m or
392 Units can be specified as b (bytes), k or kb (kilobytes), m or
393 mb (megabytes), g or gb (gigabytes).
393 mb (megabytes), g or gb (gigabytes).
394
394
395 >>> u = ui(); s = 'foo'
395 >>> u = ui(); s = 'foo'
396 >>> u.setconfig(s, 'val1', '42')
396 >>> u.setconfig(s, 'val1', '42')
397 >>> u.configbytes(s, 'val1')
397 >>> u.configbytes(s, 'val1')
398 42
398 42
399 >>> u.setconfig(s, 'val2', '42.5 kb')
399 >>> u.setconfig(s, 'val2', '42.5 kb')
400 >>> u.configbytes(s, 'val2')
400 >>> u.configbytes(s, 'val2')
401 43520
401 43520
402 >>> u.configbytes(s, 'unknown', '7 MB')
402 >>> u.configbytes(s, 'unknown', '7 MB')
403 7340032
403 7340032
404 >>> u.setconfig(s, 'invalid', 'somevalue')
404 >>> u.setconfig(s, 'invalid', 'somevalue')
405 >>> u.configbytes(s, 'invalid')
405 >>> u.configbytes(s, 'invalid')
406 Traceback (most recent call last):
406 Traceback (most recent call last):
407 ...
407 ...
408 ConfigError: foo.invalid is not a byte quantity ('somevalue')
408 ConfigError: foo.invalid is not a byte quantity ('somevalue')
409 """
409 """
410
410
411 value = self.config(section, name)
411 value = self.config(section, name)
412 if value is None:
412 if value is None:
413 if not isinstance(default, str):
413 if not isinstance(default, str):
414 return default
414 return default
415 value = default
415 value = default
416 try:
416 try:
417 return util.sizetoint(value)
417 return util.sizetoint(value)
418 except error.ParseError:
418 except error.ParseError:
419 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
419 raise error.ConfigError(_("%s.%s is not a byte quantity ('%s')")
420 % (section, name, value))
420 % (section, name, value))
421
421
422 def configlist(self, section, name, default=None, untrusted=False):
422 def configlist(self, section, name, default=None, untrusted=False):
423 """parse a configuration element as a list of comma/space separated
423 """parse a configuration element as a list of comma/space separated
424 strings
424 strings
425
425
426 >>> u = ui(); s = 'foo'
426 >>> u = ui(); s = 'foo'
427 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
427 >>> u.setconfig(s, 'list1', 'this,is "a small" ,test')
428 >>> u.configlist(s, 'list1')
428 >>> u.configlist(s, 'list1')
429 ['this', 'is', 'a small', 'test']
429 ['this', 'is', 'a small', 'test']
430 """
430 """
431
431
432 def _parse_plain(parts, s, offset):
432 def _parse_plain(parts, s, offset):
433 whitespace = False
433 whitespace = False
434 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
434 while offset < len(s) and (s[offset].isspace() or s[offset] == ','):
435 whitespace = True
435 whitespace = True
436 offset += 1
436 offset += 1
437 if offset >= len(s):
437 if offset >= len(s):
438 return None, parts, offset
438 return None, parts, offset
439 if whitespace:
439 if whitespace:
440 parts.append('')
440 parts.append('')
441 if s[offset] == '"' and not parts[-1]:
441 if s[offset] == '"' and not parts[-1]:
442 return _parse_quote, parts, offset + 1
442 return _parse_quote, parts, offset + 1
443 elif s[offset] == '"' and parts[-1][-1] == '\\':
443 elif s[offset] == '"' and parts[-1][-1] == '\\':
444 parts[-1] = parts[-1][:-1] + s[offset]
444 parts[-1] = parts[-1][:-1] + s[offset]
445 return _parse_plain, parts, offset + 1
445 return _parse_plain, parts, offset + 1
446 parts[-1] += s[offset]
446 parts[-1] += s[offset]
447 return _parse_plain, parts, offset + 1
447 return _parse_plain, parts, offset + 1
448
448
449 def _parse_quote(parts, s, offset):
449 def _parse_quote(parts, s, offset):
450 if offset < len(s) and s[offset] == '"': # ""
450 if offset < len(s) and s[offset] == '"': # ""
451 parts.append('')
451 parts.append('')
452 offset += 1
452 offset += 1
453 while offset < len(s) and (s[offset].isspace() or
453 while offset < len(s) and (s[offset].isspace() or
454 s[offset] == ','):
454 s[offset] == ','):
455 offset += 1
455 offset += 1
456 return _parse_plain, parts, offset
456 return _parse_plain, parts, offset
457
457
458 while offset < len(s) and s[offset] != '"':
458 while offset < len(s) and s[offset] != '"':
459 if (s[offset] == '\\' and offset + 1 < len(s)
459 if (s[offset] == '\\' and offset + 1 < len(s)
460 and s[offset + 1] == '"'):
460 and s[offset + 1] == '"'):
461 offset += 1
461 offset += 1
462 parts[-1] += '"'
462 parts[-1] += '"'
463 else:
463 else:
464 parts[-1] += s[offset]
464 parts[-1] += s[offset]
465 offset += 1
465 offset += 1
466
466
467 if offset >= len(s):
467 if offset >= len(s):
468 real_parts = _configlist(parts[-1])
468 real_parts = _configlist(parts[-1])
469 if not real_parts:
469 if not real_parts:
470 parts[-1] = '"'
470 parts[-1] = '"'
471 else:
471 else:
472 real_parts[0] = '"' + real_parts[0]
472 real_parts[0] = '"' + real_parts[0]
473 parts = parts[:-1]
473 parts = parts[:-1]
474 parts.extend(real_parts)
474 parts.extend(real_parts)
475 return None, parts, offset
475 return None, parts, offset
476
476
477 offset += 1
477 offset += 1
478 while offset < len(s) and s[offset] in [' ', ',']:
478 while offset < len(s) and s[offset] in [' ', ',']:
479 offset += 1
479 offset += 1
480
480
481 if offset < len(s):
481 if offset < len(s):
482 if offset + 1 == len(s) and s[offset] == '"':
482 if offset + 1 == len(s) and s[offset] == '"':
483 parts[-1] += '"'
483 parts[-1] += '"'
484 offset += 1
484 offset += 1
485 else:
485 else:
486 parts.append('')
486 parts.append('')
487 else:
487 else:
488 return None, parts, offset
488 return None, parts, offset
489
489
490 return _parse_plain, parts, offset
490 return _parse_plain, parts, offset
491
491
492 def _configlist(s):
492 def _configlist(s):
493 s = s.rstrip(' ,')
493 s = s.rstrip(' ,')
494 if not s:
494 if not s:
495 return []
495 return []
496 parser, parts, offset = _parse_plain, [''], 0
496 parser, parts, offset = _parse_plain, [''], 0
497 while parser:
497 while parser:
498 parser, parts, offset = parser(parts, s, offset)
498 parser, parts, offset = parser(parts, s, offset)
499 return parts
499 return parts
500
500
501 result = self.config(section, name, untrusted=untrusted)
501 result = self.config(section, name, untrusted=untrusted)
502 if result is None:
502 if result is None:
503 result = default or []
503 result = default or []
504 if isinstance(result, basestring):
504 if isinstance(result, basestring):
505 result = _configlist(result.lstrip(' ,\n'))
505 result = _configlist(result.lstrip(' ,\n'))
506 if result is None:
506 if result is None:
507 result = default or []
507 result = default or []
508 return result
508 return result
509
509
510 def hasconfig(self, section, name, untrusted=False):
511 return self._data(untrusted).hasitem(section, name)
512
510 def has_section(self, section, untrusted=False):
513 def has_section(self, section, untrusted=False):
511 '''tell whether section exists in config.'''
514 '''tell whether section exists in config.'''
512 return section in self._data(untrusted)
515 return section in self._data(untrusted)
513
516
514 def configitems(self, section, untrusted=False, ignoresub=False):
517 def configitems(self, section, untrusted=False, ignoresub=False):
515 items = self._data(untrusted).items(section)
518 items = self._data(untrusted).items(section)
516 if ignoresub:
519 if ignoresub:
517 newitems = {}
520 newitems = {}
518 for k, v in items:
521 for k, v in items:
519 if ':' not in k:
522 if ':' not in k:
520 newitems[k] = v
523 newitems[k] = v
521 items = newitems.items()
524 items = newitems.items()
522 if self.debugflag and not untrusted and self._reportuntrusted:
525 if self.debugflag and not untrusted and self._reportuntrusted:
523 for k, v in self._ucfg.items(section):
526 for k, v in self._ucfg.items(section):
524 if self._tcfg.get(section, k) != v:
527 if self._tcfg.get(section, k) != v:
525 self.debug("ignoring untrusted configuration option "
528 self.debug("ignoring untrusted configuration option "
526 "%s.%s = %s\n" % (section, k, v))
529 "%s.%s = %s\n" % (section, k, v))
527 return items
530 return items
528
531
529 def walkconfig(self, untrusted=False):
532 def walkconfig(self, untrusted=False):
530 cfg = self._data(untrusted)
533 cfg = self._data(untrusted)
531 for section in cfg.sections():
534 for section in cfg.sections():
532 for name, value in self.configitems(section, untrusted):
535 for name, value in self.configitems(section, untrusted):
533 yield section, name, value
536 yield section, name, value
534
537
535 def plain(self, feature=None):
538 def plain(self, feature=None):
536 '''is plain mode active?
539 '''is plain mode active?
537
540
538 Plain mode means that all configuration variables which affect
541 Plain mode means that all configuration variables which affect
539 the behavior and output of Mercurial should be
542 the behavior and output of Mercurial should be
540 ignored. Additionally, the output should be stable,
543 ignored. Additionally, the output should be stable,
541 reproducible and suitable for use in scripts or applications.
544 reproducible and suitable for use in scripts or applications.
542
545
543 The only way to trigger plain mode is by setting either the
546 The only way to trigger plain mode is by setting either the
544 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
547 `HGPLAIN' or `HGPLAINEXCEPT' environment variables.
545
548
546 The return value can either be
549 The return value can either be
547 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
550 - False if HGPLAIN is not set, or feature is in HGPLAINEXCEPT
548 - True otherwise
551 - True otherwise
549 '''
552 '''
550 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
553 if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ:
551 return False
554 return False
552 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
555 exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',')
553 if feature and exceptions:
556 if feature and exceptions:
554 return feature not in exceptions
557 return feature not in exceptions
555 return True
558 return True
556
559
557 def username(self):
560 def username(self):
558 """Return default username to be used in commits.
561 """Return default username to be used in commits.
559
562
560 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
563 Searched in this order: $HGUSER, [ui] section of hgrcs, $EMAIL
561 and stop searching if one of these is set.
564 and stop searching if one of these is set.
562 If not found and ui.askusername is True, ask the user, else use
565 If not found and ui.askusername is True, ask the user, else use
563 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
566 ($LOGNAME or $USER or $LNAME or $USERNAME) + "@full.hostname".
564 """
567 """
565 user = os.environ.get("HGUSER")
568 user = os.environ.get("HGUSER")
566 if user is None:
569 if user is None:
567 user = self.config("ui", ["username", "user"])
570 user = self.config("ui", ["username", "user"])
568 if user is not None:
571 if user is not None:
569 user = os.path.expandvars(user)
572 user = os.path.expandvars(user)
570 if user is None:
573 if user is None:
571 user = os.environ.get("EMAIL")
574 user = os.environ.get("EMAIL")
572 if user is None and self.configbool("ui", "askusername"):
575 if user is None and self.configbool("ui", "askusername"):
573 user = self.prompt(_("enter a commit username:"), default=None)
576 user = self.prompt(_("enter a commit username:"), default=None)
574 if user is None and not self.interactive():
577 if user is None and not self.interactive():
575 try:
578 try:
576 user = '%s@%s' % (util.getuser(), socket.getfqdn())
579 user = '%s@%s' % (util.getuser(), socket.getfqdn())
577 self.warn(_("no username found, using '%s' instead\n") % user)
580 self.warn(_("no username found, using '%s' instead\n") % user)
578 except KeyError:
581 except KeyError:
579 pass
582 pass
580 if not user:
583 if not user:
581 raise error.Abort(_('no username supplied'),
584 raise error.Abort(_('no username supplied'),
582 hint=_('use "hg config --edit" '
585 hint=_('use "hg config --edit" '
583 'to set your username'))
586 'to set your username'))
584 if "\n" in user:
587 if "\n" in user:
585 raise error.Abort(_("username %s contains a newline\n")
588 raise error.Abort(_("username %s contains a newline\n")
586 % repr(user))
589 % repr(user))
587 return user
590 return user
588
591
589 def shortuser(self, user):
592 def shortuser(self, user):
590 """Return a short representation of a user name or email address."""
593 """Return a short representation of a user name or email address."""
591 if not self.verbose:
594 if not self.verbose:
592 user = util.shortuser(user)
595 user = util.shortuser(user)
593 return user
596 return user
594
597
595 def expandpath(self, loc, default=None):
598 def expandpath(self, loc, default=None):
596 """Return repository location relative to cwd or from [paths]"""
599 """Return repository location relative to cwd or from [paths]"""
597 try:
600 try:
598 p = self.paths.getpath(loc)
601 p = self.paths.getpath(loc)
599 if p:
602 if p:
600 return p.rawloc
603 return p.rawloc
601 except error.RepoError:
604 except error.RepoError:
602 pass
605 pass
603
606
604 if default:
607 if default:
605 try:
608 try:
606 p = self.paths.getpath(default)
609 p = self.paths.getpath(default)
607 if p:
610 if p:
608 return p.rawloc
611 return p.rawloc
609 except error.RepoError:
612 except error.RepoError:
610 pass
613 pass
611
614
612 return loc
615 return loc
613
616
614 @util.propertycache
617 @util.propertycache
615 def paths(self):
618 def paths(self):
616 return paths(self)
619 return paths(self)
617
620
618 def pushbuffer(self, error=False, subproc=False, labeled=False):
621 def pushbuffer(self, error=False, subproc=False, labeled=False):
619 """install a buffer to capture standard output of the ui object
622 """install a buffer to capture standard output of the ui object
620
623
621 If error is True, the error output will be captured too.
624 If error is True, the error output will be captured too.
622
625
623 If subproc is True, output from subprocesses (typically hooks) will be
626 If subproc is True, output from subprocesses (typically hooks) will be
624 captured too.
627 captured too.
625
628
626 If labeled is True, any labels associated with buffered
629 If labeled is True, any labels associated with buffered
627 output will be handled. By default, this has no effect
630 output will be handled. By default, this has no effect
628 on the output returned, but extensions and GUI tools may
631 on the output returned, but extensions and GUI tools may
629 handle this argument and returned styled output. If output
632 handle this argument and returned styled output. If output
630 is being buffered so it can be captured and parsed or
633 is being buffered so it can be captured and parsed or
631 processed, labeled should not be set to True.
634 processed, labeled should not be set to True.
632 """
635 """
633 self._buffers.append([])
636 self._buffers.append([])
634 self._bufferstates.append((error, subproc, labeled))
637 self._bufferstates.append((error, subproc, labeled))
635 self._bufferapplylabels = labeled
638 self._bufferapplylabels = labeled
636
639
637 def popbuffer(self):
640 def popbuffer(self):
638 '''pop the last buffer and return the buffered output'''
641 '''pop the last buffer and return the buffered output'''
639 self._bufferstates.pop()
642 self._bufferstates.pop()
640 if self._bufferstates:
643 if self._bufferstates:
641 self._bufferapplylabels = self._bufferstates[-1][2]
644 self._bufferapplylabels = self._bufferstates[-1][2]
642 else:
645 else:
643 self._bufferapplylabels = None
646 self._bufferapplylabels = None
644
647
645 return "".join(self._buffers.pop())
648 return "".join(self._buffers.pop())
646
649
647 def write(self, *args, **opts):
650 def write(self, *args, **opts):
648 '''write args to output
651 '''write args to output
649
652
650 By default, this method simply writes to the buffer or stdout,
653 By default, this method simply writes to the buffer or stdout,
651 but extensions or GUI tools may override this method,
654 but extensions or GUI tools may override this method,
652 write_err(), popbuffer(), and label() to style output from
655 write_err(), popbuffer(), and label() to style output from
653 various parts of hg.
656 various parts of hg.
654
657
655 An optional keyword argument, "label", can be passed in.
658 An optional keyword argument, "label", can be passed in.
656 This should be a string containing label names separated by
659 This should be a string containing label names separated by
657 space. Label names take the form of "topic.type". For example,
660 space. Label names take the form of "topic.type". For example,
658 ui.debug() issues a label of "ui.debug".
661 ui.debug() issues a label of "ui.debug".
659
662
660 When labeling output for a specific command, a label of
663 When labeling output for a specific command, a label of
661 "cmdname.type" is recommended. For example, status issues
664 "cmdname.type" is recommended. For example, status issues
662 a label of "status.modified" for modified files.
665 a label of "status.modified" for modified files.
663 '''
666 '''
664 if self._buffers:
667 if self._buffers:
665 self._buffers[-1].extend(a for a in args)
668 self._buffers[-1].extend(a for a in args)
666 else:
669 else:
667 self._progclear()
670 self._progclear()
668 for a in args:
671 for a in args:
669 self.fout.write(a)
672 self.fout.write(a)
670
673
671 def write_err(self, *args, **opts):
674 def write_err(self, *args, **opts):
672 self._progclear()
675 self._progclear()
673 try:
676 try:
674 if self._bufferstates and self._bufferstates[-1][0]:
677 if self._bufferstates and self._bufferstates[-1][0]:
675 return self.write(*args, **opts)
678 return self.write(*args, **opts)
676 if not getattr(self.fout, 'closed', False):
679 if not getattr(self.fout, 'closed', False):
677 self.fout.flush()
680 self.fout.flush()
678 for a in args:
681 for a in args:
679 self.ferr.write(a)
682 self.ferr.write(a)
680 # stderr may be buffered under win32 when redirected to files,
683 # stderr may be buffered under win32 when redirected to files,
681 # including stdout.
684 # including stdout.
682 if not getattr(self.ferr, 'closed', False):
685 if not getattr(self.ferr, 'closed', False):
683 self.ferr.flush()
686 self.ferr.flush()
684 except IOError as inst:
687 except IOError as inst:
685 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
688 if inst.errno not in (errno.EPIPE, errno.EIO, errno.EBADF):
686 raise
689 raise
687
690
688 def flush(self):
691 def flush(self):
689 try: self.fout.flush()
692 try: self.fout.flush()
690 except (IOError, ValueError): pass
693 except (IOError, ValueError): pass
691 try: self.ferr.flush()
694 try: self.ferr.flush()
692 except (IOError, ValueError): pass
695 except (IOError, ValueError): pass
693
696
694 def _isatty(self, fh):
697 def _isatty(self, fh):
695 if self.configbool('ui', 'nontty', False):
698 if self.configbool('ui', 'nontty', False):
696 return False
699 return False
697 return util.isatty(fh)
700 return util.isatty(fh)
698
701
699 def interactive(self):
702 def interactive(self):
700 '''is interactive input allowed?
703 '''is interactive input allowed?
701
704
702 An interactive session is a session where input can be reasonably read
705 An interactive session is a session where input can be reasonably read
703 from `sys.stdin'. If this function returns false, any attempt to read
706 from `sys.stdin'. If this function returns false, any attempt to read
704 from stdin should fail with an error, unless a sensible default has been
707 from stdin should fail with an error, unless a sensible default has been
705 specified.
708 specified.
706
709
707 Interactiveness is triggered by the value of the `ui.interactive'
710 Interactiveness is triggered by the value of the `ui.interactive'
708 configuration variable or - if it is unset - when `sys.stdin' points
711 configuration variable or - if it is unset - when `sys.stdin' points
709 to a terminal device.
712 to a terminal device.
710
713
711 This function refers to input only; for output, see `ui.formatted()'.
714 This function refers to input only; for output, see `ui.formatted()'.
712 '''
715 '''
713 i = self.configbool("ui", "interactive", None)
716 i = self.configbool("ui", "interactive", None)
714 if i is None:
717 if i is None:
715 # some environments replace stdin without implementing isatty
718 # some environments replace stdin without implementing isatty
716 # usually those are non-interactive
719 # usually those are non-interactive
717 return self._isatty(self.fin)
720 return self._isatty(self.fin)
718
721
719 return i
722 return i
720
723
721 def termwidth(self):
724 def termwidth(self):
722 '''how wide is the terminal in columns?
725 '''how wide is the terminal in columns?
723 '''
726 '''
724 if 'COLUMNS' in os.environ:
727 if 'COLUMNS' in os.environ:
725 try:
728 try:
726 return int(os.environ['COLUMNS'])
729 return int(os.environ['COLUMNS'])
727 except ValueError:
730 except ValueError:
728 pass
731 pass
729 return util.termwidth()
732 return util.termwidth()
730
733
731 def formatted(self):
734 def formatted(self):
732 '''should formatted output be used?
735 '''should formatted output be used?
733
736
734 It is often desirable to format the output to suite the output medium.
737 It is often desirable to format the output to suite the output medium.
735 Examples of this are truncating long lines or colorizing messages.
738 Examples of this are truncating long lines or colorizing messages.
736 However, this is not often not desirable when piping output into other
739 However, this is not often not desirable when piping output into other
737 utilities, e.g. `grep'.
740 utilities, e.g. `grep'.
738
741
739 Formatted output is triggered by the value of the `ui.formatted'
742 Formatted output is triggered by the value of the `ui.formatted'
740 configuration variable or - if it is unset - when `sys.stdout' points
743 configuration variable or - if it is unset - when `sys.stdout' points
741 to a terminal device. Please note that `ui.formatted' should be
744 to a terminal device. Please note that `ui.formatted' should be
742 considered an implementation detail; it is not intended for use outside
745 considered an implementation detail; it is not intended for use outside
743 Mercurial or its extensions.
746 Mercurial or its extensions.
744
747
745 This function refers to output only; for input, see `ui.interactive()'.
748 This function refers to output only; for input, see `ui.interactive()'.
746 This function always returns false when in plain mode, see `ui.plain()'.
749 This function always returns false when in plain mode, see `ui.plain()'.
747 '''
750 '''
748 if self.plain():
751 if self.plain():
749 return False
752 return False
750
753
751 i = self.configbool("ui", "formatted", None)
754 i = self.configbool("ui", "formatted", None)
752 if i is None:
755 if i is None:
753 # some environments replace stdout without implementing isatty
756 # some environments replace stdout without implementing isatty
754 # usually those are non-interactive
757 # usually those are non-interactive
755 return self._isatty(self.fout)
758 return self._isatty(self.fout)
756
759
757 return i
760 return i
758
761
759 def _readline(self, prompt=''):
762 def _readline(self, prompt=''):
760 if self._isatty(self.fin):
763 if self._isatty(self.fin):
761 try:
764 try:
762 # magically add command line editing support, where
765 # magically add command line editing support, where
763 # available
766 # available
764 import readline
767 import readline
765 # force demandimport to really load the module
768 # force demandimport to really load the module
766 readline.read_history_file
769 readline.read_history_file
767 # windows sometimes raises something other than ImportError
770 # windows sometimes raises something other than ImportError
768 except Exception:
771 except Exception:
769 pass
772 pass
770
773
771 # call write() so output goes through subclassed implementation
774 # call write() so output goes through subclassed implementation
772 # e.g. color extension on Windows
775 # e.g. color extension on Windows
773 self.write(prompt)
776 self.write(prompt)
774
777
775 # instead of trying to emulate raw_input, swap (self.fin,
778 # instead of trying to emulate raw_input, swap (self.fin,
776 # self.fout) with (sys.stdin, sys.stdout)
779 # self.fout) with (sys.stdin, sys.stdout)
777 oldin = sys.stdin
780 oldin = sys.stdin
778 oldout = sys.stdout
781 oldout = sys.stdout
779 sys.stdin = self.fin
782 sys.stdin = self.fin
780 sys.stdout = self.fout
783 sys.stdout = self.fout
781 # prompt ' ' must exist; otherwise readline may delete entire line
784 # prompt ' ' must exist; otherwise readline may delete entire line
782 # - http://bugs.python.org/issue12833
785 # - http://bugs.python.org/issue12833
783 line = raw_input(' ')
786 line = raw_input(' ')
784 sys.stdin = oldin
787 sys.stdin = oldin
785 sys.stdout = oldout
788 sys.stdout = oldout
786
789
787 # When stdin is in binary mode on Windows, it can cause
790 # When stdin is in binary mode on Windows, it can cause
788 # raw_input() to emit an extra trailing carriage return
791 # raw_input() to emit an extra trailing carriage return
789 if os.linesep == '\r\n' and line and line[-1] == '\r':
792 if os.linesep == '\r\n' and line and line[-1] == '\r':
790 line = line[:-1]
793 line = line[:-1]
791 return line
794 return line
792
795
793 def prompt(self, msg, default="y"):
796 def prompt(self, msg, default="y"):
794 """Prompt user with msg, read response.
797 """Prompt user with msg, read response.
795 If ui is not interactive, the default is returned.
798 If ui is not interactive, the default is returned.
796 """
799 """
797 if not self.interactive():
800 if not self.interactive():
798 self.write(msg, ' ', default, "\n")
801 self.write(msg, ' ', default, "\n")
799 return default
802 return default
800 try:
803 try:
801 r = self._readline(self.label(msg, 'ui.prompt'))
804 r = self._readline(self.label(msg, 'ui.prompt'))
802 if not r:
805 if not r:
803 r = default
806 r = default
804 if self.configbool('ui', 'promptecho'):
807 if self.configbool('ui', 'promptecho'):
805 self.write(r, "\n")
808 self.write(r, "\n")
806 return r
809 return r
807 except EOFError:
810 except EOFError:
808 raise error.ResponseExpected()
811 raise error.ResponseExpected()
809
812
810 @staticmethod
813 @staticmethod
811 def extractchoices(prompt):
814 def extractchoices(prompt):
812 """Extract prompt message and list of choices from specified prompt.
815 """Extract prompt message and list of choices from specified prompt.
813
816
814 This returns tuple "(message, choices)", and "choices" is the
817 This returns tuple "(message, choices)", and "choices" is the
815 list of tuple "(response character, text without &)".
818 list of tuple "(response character, text without &)".
816
819
817 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
820 >>> ui.extractchoices("awake? $$ &Yes $$ &No")
818 ('awake? ', [('y', 'Yes'), ('n', 'No')])
821 ('awake? ', [('y', 'Yes'), ('n', 'No')])
819 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
822 >>> ui.extractchoices("line\\nbreak? $$ &Yes $$ &No")
820 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
823 ('line\\nbreak? ', [('y', 'Yes'), ('n', 'No')])
821 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
824 >>> ui.extractchoices("want lots of $$money$$?$$Ye&s$$N&o")
822 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
825 ('want lots of $$money$$?', [('s', 'Yes'), ('o', 'No')])
823 """
826 """
824
827
825 # Sadly, the prompt string may have been built with a filename
828 # Sadly, the prompt string may have been built with a filename
826 # containing "$$" so let's try to find the first valid-looking
829 # containing "$$" so let's try to find the first valid-looking
827 # prompt to start parsing. Sadly, we also can't rely on
830 # prompt to start parsing. Sadly, we also can't rely on
828 # choices containing spaces, ASCII, or basically anything
831 # choices containing spaces, ASCII, or basically anything
829 # except an ampersand followed by a character.
832 # except an ampersand followed by a character.
830 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
833 m = re.match(r'(?s)(.+?)\$\$([^\$]*&[^ \$].*)', prompt)
831 msg = m.group(1)
834 msg = m.group(1)
832 choices = [p.strip(' ') for p in m.group(2).split('$$')]
835 choices = [p.strip(' ') for p in m.group(2).split('$$')]
833 return (msg,
836 return (msg,
834 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
837 [(s[s.index('&') + 1].lower(), s.replace('&', '', 1))
835 for s in choices])
838 for s in choices])
836
839
837 def promptchoice(self, prompt, default=0):
840 def promptchoice(self, prompt, default=0):
838 """Prompt user with a message, read response, and ensure it matches
841 """Prompt user with a message, read response, and ensure it matches
839 one of the provided choices. The prompt is formatted as follows:
842 one of the provided choices. The prompt is formatted as follows:
840
843
841 "would you like fries with that (Yn)? $$ &Yes $$ &No"
844 "would you like fries with that (Yn)? $$ &Yes $$ &No"
842
845
843 The index of the choice is returned. Responses are case
846 The index of the choice is returned. Responses are case
844 insensitive. If ui is not interactive, the default is
847 insensitive. If ui is not interactive, the default is
845 returned.
848 returned.
846 """
849 """
847
850
848 msg, choices = self.extractchoices(prompt)
851 msg, choices = self.extractchoices(prompt)
849 resps = [r for r, t in choices]
852 resps = [r for r, t in choices]
850 while True:
853 while True:
851 r = self.prompt(msg, resps[default])
854 r = self.prompt(msg, resps[default])
852 if r.lower() in resps:
855 if r.lower() in resps:
853 return resps.index(r.lower())
856 return resps.index(r.lower())
854 self.write(_("unrecognized response\n"))
857 self.write(_("unrecognized response\n"))
855
858
856 def getpass(self, prompt=None, default=None):
859 def getpass(self, prompt=None, default=None):
857 if not self.interactive():
860 if not self.interactive():
858 return default
861 return default
859 try:
862 try:
860 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
863 self.write_err(self.label(prompt or _('password: '), 'ui.prompt'))
861 # disable getpass() only if explicitly specified. it's still valid
864 # disable getpass() only if explicitly specified. it's still valid
862 # to interact with tty even if fin is not a tty.
865 # to interact with tty even if fin is not a tty.
863 if self.configbool('ui', 'nontty'):
866 if self.configbool('ui', 'nontty'):
864 return self.fin.readline().rstrip('\n')
867 return self.fin.readline().rstrip('\n')
865 else:
868 else:
866 return getpass.getpass('')
869 return getpass.getpass('')
867 except EOFError:
870 except EOFError:
868 raise error.ResponseExpected()
871 raise error.ResponseExpected()
869 def status(self, *msg, **opts):
872 def status(self, *msg, **opts):
870 '''write status message to output (if ui.quiet is False)
873 '''write status message to output (if ui.quiet is False)
871
874
872 This adds an output label of "ui.status".
875 This adds an output label of "ui.status".
873 '''
876 '''
874 if not self.quiet:
877 if not self.quiet:
875 opts['label'] = opts.get('label', '') + ' ui.status'
878 opts['label'] = opts.get('label', '') + ' ui.status'
876 self.write(*msg, **opts)
879 self.write(*msg, **opts)
877 def warn(self, *msg, **opts):
880 def warn(self, *msg, **opts):
878 '''write warning message to output (stderr)
881 '''write warning message to output (stderr)
879
882
880 This adds an output label of "ui.warning".
883 This adds an output label of "ui.warning".
881 '''
884 '''
882 opts['label'] = opts.get('label', '') + ' ui.warning'
885 opts['label'] = opts.get('label', '') + ' ui.warning'
883 self.write_err(*msg, **opts)
886 self.write_err(*msg, **opts)
884 def note(self, *msg, **opts):
887 def note(self, *msg, **opts):
885 '''write note to output (if ui.verbose is True)
888 '''write note to output (if ui.verbose is True)
886
889
887 This adds an output label of "ui.note".
890 This adds an output label of "ui.note".
888 '''
891 '''
889 if self.verbose:
892 if self.verbose:
890 opts['label'] = opts.get('label', '') + ' ui.note'
893 opts['label'] = opts.get('label', '') + ' ui.note'
891 self.write(*msg, **opts)
894 self.write(*msg, **opts)
892 def debug(self, *msg, **opts):
895 def debug(self, *msg, **opts):
893 '''write debug message to output (if ui.debugflag is True)
896 '''write debug message to output (if ui.debugflag is True)
894
897
895 This adds an output label of "ui.debug".
898 This adds an output label of "ui.debug".
896 '''
899 '''
897 if self.debugflag:
900 if self.debugflag:
898 opts['label'] = opts.get('label', '') + ' ui.debug'
901 opts['label'] = opts.get('label', '') + ' ui.debug'
899 self.write(*msg, **opts)
902 self.write(*msg, **opts)
900
903
901 def edit(self, text, user, extra=None, editform=None, pending=None):
904 def edit(self, text, user, extra=None, editform=None, pending=None):
902 extra_defaults = { 'prefix': 'editor' }
905 extra_defaults = { 'prefix': 'editor' }
903 if extra is not None:
906 if extra is not None:
904 extra_defaults.update(extra)
907 extra_defaults.update(extra)
905 extra = extra_defaults
908 extra = extra_defaults
906 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
909 (fd, name) = tempfile.mkstemp(prefix='hg-' + extra['prefix'] + '-',
907 suffix=".txt", text=True)
910 suffix=".txt", text=True)
908 try:
911 try:
909 f = os.fdopen(fd, "w")
912 f = os.fdopen(fd, "w")
910 f.write(text)
913 f.write(text)
911 f.close()
914 f.close()
912
915
913 environ = {'HGUSER': user}
916 environ = {'HGUSER': user}
914 if 'transplant_source' in extra:
917 if 'transplant_source' in extra:
915 environ.update({'HGREVISION': hex(extra['transplant_source'])})
918 environ.update({'HGREVISION': hex(extra['transplant_source'])})
916 for label in ('intermediate-source', 'source', 'rebase_source'):
919 for label in ('intermediate-source', 'source', 'rebase_source'):
917 if label in extra:
920 if label in extra:
918 environ.update({'HGREVISION': extra[label]})
921 environ.update({'HGREVISION': extra[label]})
919 break
922 break
920 if editform:
923 if editform:
921 environ.update({'HGEDITFORM': editform})
924 environ.update({'HGEDITFORM': editform})
922 if pending:
925 if pending:
923 environ.update({'HG_PENDING': pending})
926 environ.update({'HG_PENDING': pending})
924
927
925 editor = self.geteditor()
928 editor = self.geteditor()
926
929
927 self.system("%s \"%s\"" % (editor, name),
930 self.system("%s \"%s\"" % (editor, name),
928 environ=environ,
931 environ=environ,
929 onerr=error.Abort, errprefix=_("edit failed"))
932 onerr=error.Abort, errprefix=_("edit failed"))
930
933
931 f = open(name)
934 f = open(name)
932 t = f.read()
935 t = f.read()
933 f.close()
936 f.close()
934 finally:
937 finally:
935 os.unlink(name)
938 os.unlink(name)
936
939
937 return t
940 return t
938
941
939 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
942 def system(self, cmd, environ=None, cwd=None, onerr=None, errprefix=None):
940 '''execute shell command with appropriate output stream. command
943 '''execute shell command with appropriate output stream. command
941 output will be redirected if fout is not stdout.
944 output will be redirected if fout is not stdout.
942 '''
945 '''
943 out = self.fout
946 out = self.fout
944 if any(s[1] for s in self._bufferstates):
947 if any(s[1] for s in self._bufferstates):
945 out = self
948 out = self
946 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
949 return util.system(cmd, environ=environ, cwd=cwd, onerr=onerr,
947 errprefix=errprefix, out=out)
950 errprefix=errprefix, out=out)
948
951
949 def traceback(self, exc=None, force=False):
952 def traceback(self, exc=None, force=False):
950 '''print exception traceback if traceback printing enabled or forced.
953 '''print exception traceback if traceback printing enabled or forced.
951 only to call in exception handler. returns true if traceback
954 only to call in exception handler. returns true if traceback
952 printed.'''
955 printed.'''
953 if self.tracebackflag or force:
956 if self.tracebackflag or force:
954 if exc is None:
957 if exc is None:
955 exc = sys.exc_info()
958 exc = sys.exc_info()
956 cause = getattr(exc[1], 'cause', None)
959 cause = getattr(exc[1], 'cause', None)
957
960
958 if cause is not None:
961 if cause is not None:
959 causetb = traceback.format_tb(cause[2])
962 causetb = traceback.format_tb(cause[2])
960 exctb = traceback.format_tb(exc[2])
963 exctb = traceback.format_tb(exc[2])
961 exconly = traceback.format_exception_only(cause[0], cause[1])
964 exconly = traceback.format_exception_only(cause[0], cause[1])
962
965
963 # exclude frame where 'exc' was chained and rethrown from exctb
966 # exclude frame where 'exc' was chained and rethrown from exctb
964 self.write_err('Traceback (most recent call last):\n',
967 self.write_err('Traceback (most recent call last):\n',
965 ''.join(exctb[:-1]),
968 ''.join(exctb[:-1]),
966 ''.join(causetb),
969 ''.join(causetb),
967 ''.join(exconly))
970 ''.join(exconly))
968 else:
971 else:
969 output = traceback.format_exception(exc[0], exc[1], exc[2])
972 output = traceback.format_exception(exc[0], exc[1], exc[2])
970 self.write_err(''.join(output))
973 self.write_err(''.join(output))
971 return self.tracebackflag or force
974 return self.tracebackflag or force
972
975
973 def geteditor(self):
976 def geteditor(self):
974 '''return editor to use'''
977 '''return editor to use'''
975 if sys.platform == 'plan9':
978 if sys.platform == 'plan9':
976 # vi is the MIPS instruction simulator on Plan 9. We
979 # vi is the MIPS instruction simulator on Plan 9. We
977 # instead default to E to plumb commit messages to
980 # instead default to E to plumb commit messages to
978 # avoid confusion.
981 # avoid confusion.
979 editor = 'E'
982 editor = 'E'
980 else:
983 else:
981 editor = 'vi'
984 editor = 'vi'
982 return (os.environ.get("HGEDITOR") or
985 return (os.environ.get("HGEDITOR") or
983 self.config("ui", "editor") or
986 self.config("ui", "editor") or
984 os.environ.get("VISUAL") or
987 os.environ.get("VISUAL") or
985 os.environ.get("EDITOR", editor))
988 os.environ.get("EDITOR", editor))
986
989
987 @util.propertycache
990 @util.propertycache
988 def _progbar(self):
991 def _progbar(self):
989 """setup the progbar singleton to the ui object"""
992 """setup the progbar singleton to the ui object"""
990 if (self.quiet or self.debugflag
993 if (self.quiet or self.debugflag
991 or self.configbool('progress', 'disable', False)
994 or self.configbool('progress', 'disable', False)
992 or not progress.shouldprint(self)):
995 or not progress.shouldprint(self)):
993 return None
996 return None
994 return getprogbar(self)
997 return getprogbar(self)
995
998
996 def _progclear(self):
999 def _progclear(self):
997 """clear progress bar output if any. use it before any output"""
1000 """clear progress bar output if any. use it before any output"""
998 if '_progbar' not in vars(self): # nothing loaded yet
1001 if '_progbar' not in vars(self): # nothing loaded yet
999 return
1002 return
1000 if self._progbar is not None and self._progbar.printed:
1003 if self._progbar is not None and self._progbar.printed:
1001 self._progbar.clear()
1004 self._progbar.clear()
1002
1005
1003 def progress(self, topic, pos, item="", unit="", total=None):
1006 def progress(self, topic, pos, item="", unit="", total=None):
1004 '''show a progress message
1007 '''show a progress message
1005
1008
1006 With stock hg, this is simply a debug message that is hidden
1009 With stock hg, this is simply a debug message that is hidden
1007 by default, but with extensions or GUI tools it may be
1010 by default, but with extensions or GUI tools it may be
1008 visible. 'topic' is the current operation, 'item' is a
1011 visible. 'topic' is the current operation, 'item' is a
1009 non-numeric marker of the current position (i.e. the currently
1012 non-numeric marker of the current position (i.e. the currently
1010 in-process file), 'pos' is the current numeric position (i.e.
1013 in-process file), 'pos' is the current numeric position (i.e.
1011 revision, bytes, etc.), unit is a corresponding unit label,
1014 revision, bytes, etc.), unit is a corresponding unit label,
1012 and total is the highest expected pos.
1015 and total is the highest expected pos.
1013
1016
1014 Multiple nested topics may be active at a time.
1017 Multiple nested topics may be active at a time.
1015
1018
1016 All topics should be marked closed by setting pos to None at
1019 All topics should be marked closed by setting pos to None at
1017 termination.
1020 termination.
1018 '''
1021 '''
1019 if self._progbar is not None:
1022 if self._progbar is not None:
1020 self._progbar.progress(topic, pos, item=item, unit=unit,
1023 self._progbar.progress(topic, pos, item=item, unit=unit,
1021 total=total)
1024 total=total)
1022 if pos is None or not self.configbool('progress', 'debug'):
1025 if pos is None or not self.configbool('progress', 'debug'):
1023 return
1026 return
1024
1027
1025 if unit:
1028 if unit:
1026 unit = ' ' + unit
1029 unit = ' ' + unit
1027 if item:
1030 if item:
1028 item = ' ' + item
1031 item = ' ' + item
1029
1032
1030 if total:
1033 if total:
1031 pct = 100.0 * pos / total
1034 pct = 100.0 * pos / total
1032 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1035 self.debug('%s:%s %s/%s%s (%4.2f%%)\n'
1033 % (topic, item, pos, total, unit, pct))
1036 % (topic, item, pos, total, unit, pct))
1034 else:
1037 else:
1035 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1038 self.debug('%s:%s %s%s\n' % (topic, item, pos, unit))
1036
1039
1037 def log(self, service, *msg, **opts):
1040 def log(self, service, *msg, **opts):
1038 '''hook for logging facility extensions
1041 '''hook for logging facility extensions
1039
1042
1040 service should be a readily-identifiable subsystem, which will
1043 service should be a readily-identifiable subsystem, which will
1041 allow filtering.
1044 allow filtering.
1042
1045
1043 *msg should be a newline-terminated format string to log, and
1046 *msg should be a newline-terminated format string to log, and
1044 then any values to %-format into that format string.
1047 then any values to %-format into that format string.
1045
1048
1046 **opts currently has no defined meanings.
1049 **opts currently has no defined meanings.
1047 '''
1050 '''
1048
1051
1049 def label(self, msg, label):
1052 def label(self, msg, label):
1050 '''style msg based on supplied label
1053 '''style msg based on supplied label
1051
1054
1052 Like ui.write(), this just returns msg unchanged, but extensions
1055 Like ui.write(), this just returns msg unchanged, but extensions
1053 and GUI tools can override it to allow styling output without
1056 and GUI tools can override it to allow styling output without
1054 writing it.
1057 writing it.
1055
1058
1056 ui.write(s, 'label') is equivalent to
1059 ui.write(s, 'label') is equivalent to
1057 ui.write(ui.label(s, 'label')).
1060 ui.write(ui.label(s, 'label')).
1058 '''
1061 '''
1059 return msg
1062 return msg
1060
1063
1061 def develwarn(self, msg, stacklevel=1):
1064 def develwarn(self, msg, stacklevel=1):
1062 """issue a developer warning message
1065 """issue a developer warning message
1063
1066
1064 Use 'stacklevel' to report the offender some layers further up in the
1067 Use 'stacklevel' to report the offender some layers further up in the
1065 stack.
1068 stack.
1066 """
1069 """
1067 msg = 'devel-warn: ' + msg
1070 msg = 'devel-warn: ' + msg
1068 stacklevel += 1 # get in develwarn
1071 stacklevel += 1 # get in develwarn
1069 if self.tracebackflag:
1072 if self.tracebackflag:
1070 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1073 util.debugstacktrace(msg, stacklevel, self.ferr, self.fout)
1071 else:
1074 else:
1072 curframe = inspect.currentframe()
1075 curframe = inspect.currentframe()
1073 calframe = inspect.getouterframes(curframe, 2)
1076 calframe = inspect.getouterframes(curframe, 2)
1074 self.write_err('%s at: %s:%s (%s)\n'
1077 self.write_err('%s at: %s:%s (%s)\n'
1075 % ((msg,) + calframe[stacklevel][1:4]))
1078 % ((msg,) + calframe[stacklevel][1:4]))
1076
1079
1077 def deprecwarn(self, msg, version):
1080 def deprecwarn(self, msg, version):
1078 """issue a deprecation warning
1081 """issue a deprecation warning
1079
1082
1080 - msg: message explaining what is deprecated and how to upgrade,
1083 - msg: message explaining what is deprecated and how to upgrade,
1081 - version: last version where the API will be supported,
1084 - version: last version where the API will be supported,
1082 """
1085 """
1083 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1086 msg += ("\n(compatibility will be dropped after Mercurial-%s,"
1084 " update your code.)") % version
1087 " update your code.)") % version
1085 self.develwarn(msg, stacklevel=2)
1088 self.develwarn(msg, stacklevel=2)
1086
1089
1087 class paths(dict):
1090 class paths(dict):
1088 """Represents a collection of paths and their configs.
1091 """Represents a collection of paths and their configs.
1089
1092
1090 Data is initially derived from ui instances and the config files they have
1093 Data is initially derived from ui instances and the config files they have
1091 loaded.
1094 loaded.
1092 """
1095 """
1093 def __init__(self, ui):
1096 def __init__(self, ui):
1094 dict.__init__(self)
1097 dict.__init__(self)
1095
1098
1096 for name, loc in ui.configitems('paths', ignoresub=True):
1099 for name, loc in ui.configitems('paths', ignoresub=True):
1097 # No location is the same as not existing.
1100 # No location is the same as not existing.
1098 if not loc:
1101 if not loc:
1099 continue
1102 continue
1100 loc, sub = ui.configsuboptions('paths', name)
1103 loc, sub = ui.configsuboptions('paths', name)
1101 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1104 self[name] = path(ui, name, rawloc=loc, suboptions=sub)
1102
1105
1103 def getpath(self, name, default=None):
1106 def getpath(self, name, default=None):
1104 """Return a ``path`` from a string, falling back to default.
1107 """Return a ``path`` from a string, falling back to default.
1105
1108
1106 ``name`` can be a named path or locations. Locations are filesystem
1109 ``name`` can be a named path or locations. Locations are filesystem
1107 paths or URIs.
1110 paths or URIs.
1108
1111
1109 Returns None if ``name`` is not a registered path, a URI, or a local
1112 Returns None if ``name`` is not a registered path, a URI, or a local
1110 path to a repo.
1113 path to a repo.
1111 """
1114 """
1112 # Only fall back to default if no path was requested.
1115 # Only fall back to default if no path was requested.
1113 if name is None:
1116 if name is None:
1114 if not default:
1117 if not default:
1115 default = ()
1118 default = ()
1116 elif not isinstance(default, (tuple, list)):
1119 elif not isinstance(default, (tuple, list)):
1117 default = (default,)
1120 default = (default,)
1118 for k in default:
1121 for k in default:
1119 try:
1122 try:
1120 return self[k]
1123 return self[k]
1121 except KeyError:
1124 except KeyError:
1122 continue
1125 continue
1123 return None
1126 return None
1124
1127
1125 # Most likely empty string.
1128 # Most likely empty string.
1126 # This may need to raise in the future.
1129 # This may need to raise in the future.
1127 if not name:
1130 if not name:
1128 return None
1131 return None
1129
1132
1130 try:
1133 try:
1131 return self[name]
1134 return self[name]
1132 except KeyError:
1135 except KeyError:
1133 # Try to resolve as a local path or URI.
1136 # Try to resolve as a local path or URI.
1134 try:
1137 try:
1135 # We don't pass sub-options in, so no need to pass ui instance.
1138 # We don't pass sub-options in, so no need to pass ui instance.
1136 return path(None, None, rawloc=name)
1139 return path(None, None, rawloc=name)
1137 except ValueError:
1140 except ValueError:
1138 raise error.RepoError(_('repository %s does not exist') %
1141 raise error.RepoError(_('repository %s does not exist') %
1139 name)
1142 name)
1140
1143
1141 _pathsuboptions = {}
1144 _pathsuboptions = {}
1142
1145
1143 def pathsuboption(option, attr):
1146 def pathsuboption(option, attr):
1144 """Decorator used to declare a path sub-option.
1147 """Decorator used to declare a path sub-option.
1145
1148
1146 Arguments are the sub-option name and the attribute it should set on
1149 Arguments are the sub-option name and the attribute it should set on
1147 ``path`` instances.
1150 ``path`` instances.
1148
1151
1149 The decorated function will receive as arguments a ``ui`` instance,
1152 The decorated function will receive as arguments a ``ui`` instance,
1150 ``path`` instance, and the string value of this option from the config.
1153 ``path`` instance, and the string value of this option from the config.
1151 The function should return the value that will be set on the ``path``
1154 The function should return the value that will be set on the ``path``
1152 instance.
1155 instance.
1153
1156
1154 This decorator can be used to perform additional verification of
1157 This decorator can be used to perform additional verification of
1155 sub-options and to change the type of sub-options.
1158 sub-options and to change the type of sub-options.
1156 """
1159 """
1157 def register(func):
1160 def register(func):
1158 _pathsuboptions[option] = (attr, func)
1161 _pathsuboptions[option] = (attr, func)
1159 return func
1162 return func
1160 return register
1163 return register
1161
1164
1162 @pathsuboption('pushurl', 'pushloc')
1165 @pathsuboption('pushurl', 'pushloc')
1163 def pushurlpathoption(ui, path, value):
1166 def pushurlpathoption(ui, path, value):
1164 u = util.url(value)
1167 u = util.url(value)
1165 # Actually require a URL.
1168 # Actually require a URL.
1166 if not u.scheme:
1169 if not u.scheme:
1167 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1170 ui.warn(_('(paths.%s:pushurl not a URL; ignoring)\n') % path.name)
1168 return None
1171 return None
1169
1172
1170 # Don't support the #foo syntax in the push URL to declare branch to
1173 # Don't support the #foo syntax in the push URL to declare branch to
1171 # push.
1174 # push.
1172 if u.fragment:
1175 if u.fragment:
1173 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1176 ui.warn(_('("#fragment" in paths.%s:pushurl not supported; '
1174 'ignoring)\n') % path.name)
1177 'ignoring)\n') % path.name)
1175 u.fragment = None
1178 u.fragment = None
1176
1179
1177 return str(u)
1180 return str(u)
1178
1181
1179 class path(object):
1182 class path(object):
1180 """Represents an individual path and its configuration."""
1183 """Represents an individual path and its configuration."""
1181
1184
1182 def __init__(self, ui, name, rawloc=None, suboptions=None):
1185 def __init__(self, ui, name, rawloc=None, suboptions=None):
1183 """Construct a path from its config options.
1186 """Construct a path from its config options.
1184
1187
1185 ``ui`` is the ``ui`` instance the path is coming from.
1188 ``ui`` is the ``ui`` instance the path is coming from.
1186 ``name`` is the symbolic name of the path.
1189 ``name`` is the symbolic name of the path.
1187 ``rawloc`` is the raw location, as defined in the config.
1190 ``rawloc`` is the raw location, as defined in the config.
1188 ``pushloc`` is the raw locations pushes should be made to.
1191 ``pushloc`` is the raw locations pushes should be made to.
1189
1192
1190 If ``name`` is not defined, we require that the location be a) a local
1193 If ``name`` is not defined, we require that the location be a) a local
1191 filesystem path with a .hg directory or b) a URL. If not,
1194 filesystem path with a .hg directory or b) a URL. If not,
1192 ``ValueError`` is raised.
1195 ``ValueError`` is raised.
1193 """
1196 """
1194 if not rawloc:
1197 if not rawloc:
1195 raise ValueError('rawloc must be defined')
1198 raise ValueError('rawloc must be defined')
1196
1199
1197 # Locations may define branches via syntax <base>#<branch>.
1200 # Locations may define branches via syntax <base>#<branch>.
1198 u = util.url(rawloc)
1201 u = util.url(rawloc)
1199 branch = None
1202 branch = None
1200 if u.fragment:
1203 if u.fragment:
1201 branch = u.fragment
1204 branch = u.fragment
1202 u.fragment = None
1205 u.fragment = None
1203
1206
1204 self.url = u
1207 self.url = u
1205 self.branch = branch
1208 self.branch = branch
1206
1209
1207 self.name = name
1210 self.name = name
1208 self.rawloc = rawloc
1211 self.rawloc = rawloc
1209 self.loc = str(u)
1212 self.loc = str(u)
1210
1213
1211 # When given a raw location but not a symbolic name, validate the
1214 # When given a raw location but not a symbolic name, validate the
1212 # location is valid.
1215 # location is valid.
1213 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1216 if not name and not u.scheme and not self._isvalidlocalpath(self.loc):
1214 raise ValueError('location is not a URL or path to a local '
1217 raise ValueError('location is not a URL or path to a local '
1215 'repo: %s' % rawloc)
1218 'repo: %s' % rawloc)
1216
1219
1217 suboptions = suboptions or {}
1220 suboptions = suboptions or {}
1218
1221
1219 # Now process the sub-options. If a sub-option is registered, its
1222 # Now process the sub-options. If a sub-option is registered, its
1220 # attribute will always be present. The value will be None if there
1223 # attribute will always be present. The value will be None if there
1221 # was no valid sub-option.
1224 # was no valid sub-option.
1222 for suboption, (attr, func) in _pathsuboptions.iteritems():
1225 for suboption, (attr, func) in _pathsuboptions.iteritems():
1223 if suboption not in suboptions:
1226 if suboption not in suboptions:
1224 setattr(self, attr, None)
1227 setattr(self, attr, None)
1225 continue
1228 continue
1226
1229
1227 value = func(ui, self, suboptions[suboption])
1230 value = func(ui, self, suboptions[suboption])
1228 setattr(self, attr, value)
1231 setattr(self, attr, value)
1229
1232
1230 def _isvalidlocalpath(self, path):
1233 def _isvalidlocalpath(self, path):
1231 """Returns True if the given path is a potentially valid repository.
1234 """Returns True if the given path is a potentially valid repository.
1232 This is its own function so that extensions can change the definition of
1235 This is its own function so that extensions can change the definition of
1233 'valid' in this case (like when pulling from a git repo into a hg
1236 'valid' in this case (like when pulling from a git repo into a hg
1234 one)."""
1237 one)."""
1235 return os.path.isdir(os.path.join(path, '.hg'))
1238 return os.path.isdir(os.path.join(path, '.hg'))
1236
1239
1237 @property
1240 @property
1238 def suboptions(self):
1241 def suboptions(self):
1239 """Return sub-options and their values for this path.
1242 """Return sub-options and their values for this path.
1240
1243
1241 This is intended to be used for presentation purposes.
1244 This is intended to be used for presentation purposes.
1242 """
1245 """
1243 d = {}
1246 d = {}
1244 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1247 for subopt, (attr, _func) in _pathsuboptions.iteritems():
1245 value = getattr(self, attr)
1248 value = getattr(self, attr)
1246 if value is not None:
1249 if value is not None:
1247 d[subopt] = value
1250 d[subopt] = value
1248 return d
1251 return d
1249
1252
1250 # we instantiate one globally shared progress bar to avoid
1253 # we instantiate one globally shared progress bar to avoid
1251 # competing progress bars when multiple UI objects get created
1254 # competing progress bars when multiple UI objects get created
1252 _progresssingleton = None
1255 _progresssingleton = None
1253
1256
1254 def getprogbar(ui):
1257 def getprogbar(ui):
1255 global _progresssingleton
1258 global _progresssingleton
1256 if _progresssingleton is None:
1259 if _progresssingleton is None:
1257 # passing 'ui' object to the singleton is fishy,
1260 # passing 'ui' object to the singleton is fishy,
1258 # this is how the extension used to work but feel free to rework it.
1261 # this is how the extension used to work but feel free to rework it.
1259 _progresssingleton = progress.progbar(ui)
1262 _progresssingleton = progress.progbar(ui)
1260 return _progresssingleton
1263 return _progresssingleton
General Comments 0
You need to be logged in to leave comments. Login now