##// END OF EJS Templates
config: discard "%unset" values defined in the other files read in previously...
FUJIWARA Katsunori -
r19087:7d82ad4b stable
parent child Browse files
Show More
@@ -1,183 +1,189 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 i18n import _
8 from i18n import _
9 import error, util
9 import error, util
10 import os, errno
10 import 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 def keys(self):
38 def keys(self):
39 return self._list
39 return self._list
40 def iterkeys(self):
40 def iterkeys(self):
41 return self._list.__iter__()
41 return self._list.__iter__()
42
42
43 class config(object):
43 class config(object):
44 def __init__(self, data=None):
44 def __init__(self, data=None):
45 self._data = {}
45 self._data = {}
46 self._source = {}
46 self._source = {}
47 self._unset = []
47 if data:
48 if data:
48 for k in data._data:
49 for k in data._data:
49 self._data[k] = data[k].copy()
50 self._data[k] = data[k].copy()
50 self._source = data._source.copy()
51 self._source = data._source.copy()
51 def copy(self):
52 def copy(self):
52 return config(self)
53 return config(self)
53 def __contains__(self, section):
54 def __contains__(self, section):
54 return section in self._data
55 return section in self._data
55 def __getitem__(self, section):
56 def __getitem__(self, section):
56 return self._data.get(section, {})
57 return self._data.get(section, {})
57 def __iter__(self):
58 def __iter__(self):
58 for d in self.sections():
59 for d in self.sections():
59 yield d
60 yield d
60 def update(self, src):
61 def update(self, src):
62 for s, n in src._unset:
63 if s in self and n in self._data[s]:
64 del self._data[s][n]
65 del self._source[(s, n)]
61 for s in src:
66 for s in src:
62 if s not in self:
67 if s not in self:
63 self._data[s] = sortdict()
68 self._data[s] = sortdict()
64 self._data[s].update(src._data[s])
69 self._data[s].update(src._data[s])
65 self._source.update(src._source)
70 self._source.update(src._source)
66 def get(self, section, item, default=None):
71 def get(self, section, item, default=None):
67 return self._data.get(section, {}).get(item, default)
72 return self._data.get(section, {}).get(item, default)
68
73
69 def backup(self, section, item):
74 def backup(self, section, item):
70 """return a tuple allowing restore to reinstall a previous value
75 """return a tuple allowing restore to reinstall a previous value
71
76
72 The main reason we need it is because it handles the "no data" case.
77 The main reason we need it is because it handles the "no data" case.
73 """
78 """
74 try:
79 try:
75 value = self._data[section][item]
80 value = self._data[section][item]
76 source = self.source(section, item)
81 source = self.source(section, item)
77 return (section, item, value, source)
82 return (section, item, value, source)
78 except KeyError:
83 except KeyError:
79 return (section, item)
84 return (section, item)
80
85
81 def source(self, section, item):
86 def source(self, section, item):
82 return self._source.get((section, item), "")
87 return self._source.get((section, item), "")
83 def sections(self):
88 def sections(self):
84 return sorted(self._data.keys())
89 return sorted(self._data.keys())
85 def items(self, section):
90 def items(self, section):
86 return self._data.get(section, {}).items()
91 return self._data.get(section, {}).items()
87 def set(self, section, item, value, source=""):
92 def set(self, section, item, value, source=""):
88 if section not in self:
93 if section not in self:
89 self._data[section] = sortdict()
94 self._data[section] = sortdict()
90 self._data[section][item] = value
95 self._data[section][item] = value
91 self._source[(section, item)] = source
96 self._source[(section, item)] = source
92
97
93 def restore(self, data):
98 def restore(self, data):
94 """restore data returned by self.backup"""
99 """restore data returned by self.backup"""
95 if len(data) == 4:
100 if len(data) == 4:
96 # restore old data
101 # restore old data
97 section, item, value, source = data
102 section, item, value, source = data
98 self._data[section][item] = value
103 self._data[section][item] = value
99 self._source[(section, item)] = source
104 self._source[(section, item)] = source
100 else:
105 else:
101 # no data before, remove everything
106 # no data before, remove everything
102 section, item = data
107 section, item = data
103 if section in self._data:
108 if section in self._data:
104 del self._data[section][item]
109 del self._data[section][item]
105 self._source.pop((section, item), None)
110 self._source.pop((section, item), None)
106
111
107 def parse(self, src, data, sections=None, remap=None, include=None):
112 def parse(self, src, data, sections=None, remap=None, include=None):
108 sectionre = util.compilere(r'\[([^\[]+)\]')
113 sectionre = util.compilere(r'\[([^\[]+)\]')
109 itemre = util.compilere(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
114 itemre = util.compilere(r'([^=\s][^=]*?)\s*=\s*(.*\S|)')
110 contre = util.compilere(r'\s+(\S|\S.*\S)\s*$')
115 contre = util.compilere(r'\s+(\S|\S.*\S)\s*$')
111 emptyre = util.compilere(r'(;|#|\s*$)')
116 emptyre = util.compilere(r'(;|#|\s*$)')
112 commentre = util.compilere(r'(;|#)')
117 commentre = util.compilere(r'(;|#)')
113 unsetre = util.compilere(r'%unset\s+(\S+)')
118 unsetre = util.compilere(r'%unset\s+(\S+)')
114 includere = util.compilere(r'%include\s+(\S|\S.*\S)\s*$')
119 includere = util.compilere(r'%include\s+(\S|\S.*\S)\s*$')
115 section = ""
120 section = ""
116 item = None
121 item = None
117 line = 0
122 line = 0
118 cont = False
123 cont = False
119
124
120 for l in data.splitlines(True):
125 for l in data.splitlines(True):
121 line += 1
126 line += 1
122 if line == 1 and l.startswith('\xef\xbb\xbf'):
127 if line == 1 and l.startswith('\xef\xbb\xbf'):
123 # Someone set us up the BOM
128 # Someone set us up the BOM
124 l = l[3:]
129 l = l[3:]
125 if cont:
130 if cont:
126 if commentre.match(l):
131 if commentre.match(l):
127 continue
132 continue
128 m = contre.match(l)
133 m = contre.match(l)
129 if m:
134 if m:
130 if sections and section not in sections:
135 if sections and section not in sections:
131 continue
136 continue
132 v = self.get(section, item) + "\n" + m.group(1)
137 v = self.get(section, item) + "\n" + m.group(1)
133 self.set(section, item, v, "%s:%d" % (src, line))
138 self.set(section, item, v, "%s:%d" % (src, line))
134 continue
139 continue
135 item = None
140 item = None
136 cont = False
141 cont = False
137 m = includere.match(l)
142 m = includere.match(l)
138 if m:
143 if m:
139 inc = util.expandpath(m.group(1))
144 inc = util.expandpath(m.group(1))
140 base = os.path.dirname(src)
145 base = os.path.dirname(src)
141 inc = os.path.normpath(os.path.join(base, inc))
146 inc = os.path.normpath(os.path.join(base, inc))
142 if include:
147 if include:
143 try:
148 try:
144 include(inc, remap=remap, sections=sections)
149 include(inc, remap=remap, sections=sections)
145 except IOError, inst:
150 except IOError, inst:
146 if inst.errno != errno.ENOENT:
151 if inst.errno != errno.ENOENT:
147 raise error.ParseError(_("cannot include %s (%s)")
152 raise error.ParseError(_("cannot include %s (%s)")
148 % (inc, inst.strerror),
153 % (inc, inst.strerror),
149 "%s:%s" % (src, line))
154 "%s:%s" % (src, line))
150 continue
155 continue
151 if emptyre.match(l):
156 if emptyre.match(l):
152 continue
157 continue
153 m = sectionre.match(l)
158 m = sectionre.match(l)
154 if m:
159 if m:
155 section = m.group(1)
160 section = m.group(1)
156 if remap:
161 if remap:
157 section = remap.get(section, section)
162 section = remap.get(section, section)
158 if section not in self:
163 if section not in self:
159 self._data[section] = sortdict()
164 self._data[section] = sortdict()
160 continue
165 continue
161 m = itemre.match(l)
166 m = itemre.match(l)
162 if m:
167 if m:
163 item = m.group(1)
168 item = m.group(1)
164 cont = True
169 cont = True
165 if sections and section not in sections:
170 if sections and section not in sections:
166 continue
171 continue
167 self.set(section, item, m.group(2), "%s:%d" % (src, line))
172 self.set(section, item, m.group(2), "%s:%d" % (src, line))
168 continue
173 continue
169 m = unsetre.match(l)
174 m = unsetre.match(l)
170 if m:
175 if m:
171 name = m.group(1)
176 name = m.group(1)
172 if sections and section not in sections:
177 if sections and section not in sections:
173 continue
178 continue
174 if self.get(section, name) is not None:
179 if self.get(section, name) is not None:
175 del self._data[section][name]
180 del self._data[section][name]
181 self._unset.append((section, name))
176 continue
182 continue
177
183
178 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
184 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
179
185
180 def read(self, path, fp=None, sections=None, remap=None):
186 def read(self, path, fp=None, sections=None, remap=None):
181 if not fp:
187 if not fp:
182 fp = util.posixfile(path)
188 fp = util.posixfile(path)
183 self.parse(path, fp.read(), sections, remap, self.read)
189 self.parse(path, fp.read(), sections, remap, self.read)
@@ -1,13 +1,44 b''
1 hide outer repo
1 hide outer repo
2 $ hg init
2 $ hg init
3
3
4 Test case sensitive configuration
4 Test case sensitive configuration
5
5
6 $ echo '[Section]' >> $HGRCPATH
6 $ echo '[Section]' >> $HGRCPATH
7 $ echo 'KeY = Case Sensitive' >> $HGRCPATH
7 $ echo 'KeY = Case Sensitive' >> $HGRCPATH
8 $ echo 'key = lower case' >> $HGRCPATH
8 $ echo 'key = lower case' >> $HGRCPATH
9
9
10 $ hg showconfig Section
10 $ hg showconfig Section
11 Section.KeY=Case Sensitive
11 Section.KeY=Case Sensitive
12 Section.key=lower case
12 Section.key=lower case
13
13
14 Test "%unset"
15
16 $ cat >> $HGRCPATH <<EOF
17 > [unsettest]
18 > local-hgrcpath = should be unset (HGRCPATH)
19 > %unset local-hgrcpath
20 >
21 > global = should be unset (HGRCPATH)
22 >
23 > both = should be unset (HGRCPATH)
24 >
25 > set-after-unset = should be unset (HGRCPATH)
26 > EOF
27
28 $ cat >> .hg/hgrc <<EOF
29 > [unsettest]
30 > local-hgrc = should be unset (.hg/hgrc)
31 > %unset local-hgrc
32 >
33 > %unset global
34 >
35 > both = should be unset (.hg/hgrc)
36 > %unset both
37 >
38 > set-after-unset = should be unset (.hg/hgrc)
39 > %unset set-after-unset
40 > set-after-unset = should be set (.hg/hgrc)
41 > EOF
42
43 $ hg showconfig unsettest
44 unsettest.set-after-unset=should be set (.hg/hgrc)
General Comments 0
You need to be logged in to leave comments. Login now