##// END OF EJS Templates
config: remove now-unused support for "includepaths"...
Martin von Zweigbergk -
r45814:3f542427 default
parent child Browse files
Show More
@@ -1,315 +1,311 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 .pycompat import getattr
14 from .pycompat import getattr
15 from . import (
15 from . import (
16 encoding,
16 encoding,
17 error,
17 error,
18 pycompat,
18 pycompat,
19 util,
19 util,
20 )
20 )
21
21
22
22
23 class config(object):
23 class config(object):
24 def __init__(self, data=None, includepaths=None):
24 def __init__(self, data=None):
25 self._data = {}
25 self._data = {}
26 self._unset = []
26 self._unset = []
27 self._includepaths = includepaths or []
28 if data:
27 if data:
29 for k in data._data:
28 for k in data._data:
30 self._data[k] = data[k].copy()
29 self._data[k] = data[k].copy()
31 self._source = data._source.copy()
30 self._source = data._source.copy()
32 else:
31 else:
33 self._source = util.cowdict()
32 self._source = util.cowdict()
34
33
35 def copy(self):
34 def copy(self):
36 return config(self)
35 return config(self)
37
36
38 def __contains__(self, section):
37 def __contains__(self, section):
39 return section in self._data
38 return section in self._data
40
39
41 def hasitem(self, section, item):
40 def hasitem(self, section, item):
42 return item in self._data.get(section, {})
41 return item in self._data.get(section, {})
43
42
44 def __getitem__(self, section):
43 def __getitem__(self, section):
45 return self._data.get(section, {})
44 return self._data.get(section, {})
46
45
47 def __iter__(self):
46 def __iter__(self):
48 for d in self.sections():
47 for d in self.sections():
49 yield d
48 yield d
50
49
51 def update(self, src):
50 def update(self, src):
52 self._source = self._source.preparewrite()
51 self._source = self._source.preparewrite()
53 for s, n in src._unset:
52 for s, n in src._unset:
54 ds = self._data.get(s, None)
53 ds = self._data.get(s, None)
55 if ds is not None and n in ds:
54 if ds is not None and n in ds:
56 self._data[s] = ds.preparewrite()
55 self._data[s] = ds.preparewrite()
57 del self._data[s][n]
56 del self._data[s][n]
58 del self._source[(s, n)]
57 del self._source[(s, n)]
59 for s in src:
58 for s in src:
60 ds = self._data.get(s, None)
59 ds = self._data.get(s, None)
61 if ds:
60 if ds:
62 self._data[s] = ds.preparewrite()
61 self._data[s] = ds.preparewrite()
63 else:
62 else:
64 self._data[s] = util.cowsortdict()
63 self._data[s] = util.cowsortdict()
65 self._data[s].update(src._data[s])
64 self._data[s].update(src._data[s])
66 self._source.update(src._source)
65 self._source.update(src._source)
67
66
68 def get(self, section, item, default=None):
67 def get(self, section, item, default=None):
69 return self._data.get(section, {}).get(item, default)
68 return self._data.get(section, {}).get(item, default)
70
69
71 def backup(self, section, item):
70 def backup(self, section, item):
72 """return a tuple allowing restore to reinstall a previous value
71 """return a tuple allowing restore to reinstall a previous value
73
72
74 The main reason we need it is because it handles the "no data" case.
73 The main reason we need it is because it handles the "no data" case.
75 """
74 """
76 try:
75 try:
77 value = self._data[section][item]
76 value = self._data[section][item]
78 source = self.source(section, item)
77 source = self.source(section, item)
79 return (section, item, value, source)
78 return (section, item, value, source)
80 except KeyError:
79 except KeyError:
81 return (section, item)
80 return (section, item)
82
81
83 def source(self, section, item):
82 def source(self, section, item):
84 return self._source.get((section, item), b"")
83 return self._source.get((section, item), b"")
85
84
86 def sections(self):
85 def sections(self):
87 return sorted(self._data.keys())
86 return sorted(self._data.keys())
88
87
89 def items(self, section):
88 def items(self, section):
90 return list(pycompat.iteritems(self._data.get(section, {})))
89 return list(pycompat.iteritems(self._data.get(section, {})))
91
90
92 def set(self, section, item, value, source=b""):
91 def set(self, section, item, value, source=b""):
93 if pycompat.ispy3:
92 if pycompat.ispy3:
94 assert not isinstance(
93 assert not isinstance(
95 section, str
94 section, str
96 ), b'config section may not be unicode strings on Python 3'
95 ), b'config section may not be unicode strings on Python 3'
97 assert not isinstance(
96 assert not isinstance(
98 item, str
97 item, str
99 ), b'config item may not be unicode strings on Python 3'
98 ), b'config item may not be unicode strings on Python 3'
100 assert not isinstance(
99 assert not isinstance(
101 value, str
100 value, str
102 ), b'config values may not be unicode strings on Python 3'
101 ), b'config values may not be unicode strings on Python 3'
103 if section not in self:
102 if section not in self:
104 self._data[section] = util.cowsortdict()
103 self._data[section] = util.cowsortdict()
105 else:
104 else:
106 self._data[section] = self._data[section].preparewrite()
105 self._data[section] = self._data[section].preparewrite()
107 self._data[section][item] = value
106 self._data[section][item] = value
108 if source:
107 if source:
109 self._source = self._source.preparewrite()
108 self._source = self._source.preparewrite()
110 self._source[(section, item)] = source
109 self._source[(section, item)] = source
111
110
112 def restore(self, data):
111 def restore(self, data):
113 """restore data returned by self.backup"""
112 """restore data returned by self.backup"""
114 self._source = self._source.preparewrite()
113 self._source = self._source.preparewrite()
115 if len(data) == 4:
114 if len(data) == 4:
116 # restore old data
115 # restore old data
117 section, item, value, source = data
116 section, item, value, source = data
118 self._data[section] = self._data[section].preparewrite()
117 self._data[section] = self._data[section].preparewrite()
119 self._data[section][item] = value
118 self._data[section][item] = value
120 self._source[(section, item)] = source
119 self._source[(section, item)] = source
121 else:
120 else:
122 # no data before, remove everything
121 # no data before, remove everything
123 section, item = data
122 section, item = data
124 if section in self._data:
123 if section in self._data:
125 self._data[section].pop(item, None)
124 self._data[section].pop(item, None)
126 self._source.pop((section, item), None)
125 self._source.pop((section, item), None)
127
126
128 def parse(self, src, data, sections=None, remap=None, include=None):
127 def parse(self, src, data, sections=None, remap=None, include=None):
129 sectionre = util.re.compile(br'\[([^\[]+)\]')
128 sectionre = util.re.compile(br'\[([^\[]+)\]')
130 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
129 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
131 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
130 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
132 emptyre = util.re.compile(br'(;|#|\s*$)')
131 emptyre = util.re.compile(br'(;|#|\s*$)')
133 commentre = util.re.compile(br'(;|#)')
132 commentre = util.re.compile(br'(;|#)')
134 unsetre = util.re.compile(br'%unset\s+(\S+)')
133 unsetre = util.re.compile(br'%unset\s+(\S+)')
135 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
134 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
136 section = b""
135 section = b""
137 item = None
136 item = None
138 line = 0
137 line = 0
139 cont = False
138 cont = False
140
139
141 if remap:
140 if remap:
142 section = remap.get(section, section)
141 section = remap.get(section, section)
143
142
144 for l in data.splitlines(True):
143 for l in data.splitlines(True):
145 line += 1
144 line += 1
146 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
145 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
147 # Someone set us up the BOM
146 # Someone set us up the BOM
148 l = l[3:]
147 l = l[3:]
149 if cont:
148 if cont:
150 if commentre.match(l):
149 if commentre.match(l):
151 continue
150 continue
152 m = contre.match(l)
151 m = contre.match(l)
153 if m:
152 if m:
154 if sections and section not in sections:
153 if sections and section not in sections:
155 continue
154 continue
156 v = self.get(section, item) + b"\n" + m.group(1)
155 v = self.get(section, item) + b"\n" + m.group(1)
157 self.set(section, item, v, b"%s:%d" % (src, line))
156 self.set(section, item, v, b"%s:%d" % (src, line))
158 continue
157 continue
159 item = None
158 item = None
160 cont = False
159 cont = False
161 m = includere.match(l)
160 m = includere.match(l)
162
161
163 if m and include:
162 if m and include:
164 expanded = util.expandpath(m.group(1))
163 expanded = util.expandpath(m.group(1))
165 includepaths = [os.path.dirname(src)] + self._includepaths
164 inc = os.path.normpath(
166
165 os.path.join(os.path.dirname(src), expanded)
167 for base in includepaths:
166 )
168 inc = os.path.normpath(os.path.join(base, expanded))
167 try:
169
168 include(expanded, inc, remap=remap, sections=sections)
170 try:
169 except IOError as inst:
171 include(expanded, inc, remap=remap, sections=sections)
170 if inst.errno != errno.ENOENT:
172 break
171 raise error.ParseError(
173 except IOError as inst:
172 _(b"cannot include %s (%s)")
174 if inst.errno != errno.ENOENT:
173 % (inc, encoding.strtolocal(inst.strerror)),
175 raise error.ParseError(
174 b"%s:%d" % (src, line),
176 _(b"cannot include %s (%s)")
175 )
177 % (inc, encoding.strtolocal(inst.strerror)),
178 b"%s:%d" % (src, line),
179 )
180 continue
176 continue
181 if emptyre.match(l):
177 if emptyre.match(l):
182 continue
178 continue
183 m = sectionre.match(l)
179 m = sectionre.match(l)
184 if m:
180 if m:
185 section = m.group(1)
181 section = m.group(1)
186 if remap:
182 if remap:
187 section = remap.get(section, section)
183 section = remap.get(section, section)
188 if section not in self:
184 if section not in self:
189 self._data[section] = util.cowsortdict()
185 self._data[section] = util.cowsortdict()
190 continue
186 continue
191 m = itemre.match(l)
187 m = itemre.match(l)
192 if m:
188 if m:
193 item = m.group(1)
189 item = m.group(1)
194 cont = True
190 cont = True
195 if sections and section not in sections:
191 if sections and section not in sections:
196 continue
192 continue
197 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
193 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
198 continue
194 continue
199 m = unsetre.match(l)
195 m = unsetre.match(l)
200 if m:
196 if m:
201 name = m.group(1)
197 name = m.group(1)
202 if sections and section not in sections:
198 if sections and section not in sections:
203 continue
199 continue
204 if self.get(section, name) is not None:
200 if self.get(section, name) is not None:
205 self._data[section] = self._data[section].preparewrite()
201 self._data[section] = self._data[section].preparewrite()
206 del self._data[section][name]
202 del self._data[section][name]
207 self._unset.append((section, name))
203 self._unset.append((section, name))
208 continue
204 continue
209
205
210 raise error.ParseError(l.rstrip(), (b"%s:%d" % (src, line)))
206 raise error.ParseError(l.rstrip(), (b"%s:%d" % (src, line)))
211
207
212 def read(self, path, fp=None, sections=None, remap=None):
208 def read(self, path, fp=None, sections=None, remap=None):
213 if not fp:
209 if not fp:
214 fp = util.posixfile(path, b'rb')
210 fp = util.posixfile(path, b'rb')
215 assert getattr(fp, 'mode', 'rb') == 'rb', (
211 assert getattr(fp, 'mode', 'rb') == 'rb', (
216 b'config files must be opened in binary mode, got fp=%r mode=%r'
212 b'config files must be opened in binary mode, got fp=%r mode=%r'
217 % (fp, fp.mode,)
213 % (fp, fp.mode,)
218 )
214 )
219
215
220 def include(rel, abs, remap, sections):
216 def include(rel, abs, remap, sections):
221 self.read(abs, remap=remap, sections=sections)
217 self.read(abs, remap=remap, sections=sections)
222
218
223 self.parse(
219 self.parse(
224 path, fp.read(), sections=sections, remap=remap, include=include
220 path, fp.read(), sections=sections, remap=remap, include=include
225 )
221 )
226
222
227
223
228 def parselist(value):
224 def parselist(value):
229 """parse a configuration value as a list of comma/space separated strings
225 """parse a configuration value as a list of comma/space separated strings
230
226
231 >>> parselist(b'this,is "a small" ,test')
227 >>> parselist(b'this,is "a small" ,test')
232 ['this', 'is', 'a small', 'test']
228 ['this', 'is', 'a small', 'test']
233 """
229 """
234
230
235 def _parse_plain(parts, s, offset):
231 def _parse_plain(parts, s, offset):
236 whitespace = False
232 whitespace = False
237 while offset < len(s) and (
233 while offset < len(s) and (
238 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
234 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
239 ):
235 ):
240 whitespace = True
236 whitespace = True
241 offset += 1
237 offset += 1
242 if offset >= len(s):
238 if offset >= len(s):
243 return None, parts, offset
239 return None, parts, offset
244 if whitespace:
240 if whitespace:
245 parts.append(b'')
241 parts.append(b'')
246 if s[offset : offset + 1] == b'"' and not parts[-1]:
242 if s[offset : offset + 1] == b'"' and not parts[-1]:
247 return _parse_quote, parts, offset + 1
243 return _parse_quote, parts, offset + 1
248 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
244 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
249 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
245 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
250 return _parse_plain, parts, offset + 1
246 return _parse_plain, parts, offset + 1
251 parts[-1] += s[offset : offset + 1]
247 parts[-1] += s[offset : offset + 1]
252 return _parse_plain, parts, offset + 1
248 return _parse_plain, parts, offset + 1
253
249
254 def _parse_quote(parts, s, offset):
250 def _parse_quote(parts, s, offset):
255 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
251 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
256 parts.append(b'')
252 parts.append(b'')
257 offset += 1
253 offset += 1
258 while offset < len(s) and (
254 while offset < len(s) and (
259 s[offset : offset + 1].isspace()
255 s[offset : offset + 1].isspace()
260 or s[offset : offset + 1] == b','
256 or s[offset : offset + 1] == b','
261 ):
257 ):
262 offset += 1
258 offset += 1
263 return _parse_plain, parts, offset
259 return _parse_plain, parts, offset
264
260
265 while offset < len(s) and s[offset : offset + 1] != b'"':
261 while offset < len(s) and s[offset : offset + 1] != b'"':
266 if (
262 if (
267 s[offset : offset + 1] == b'\\'
263 s[offset : offset + 1] == b'\\'
268 and offset + 1 < len(s)
264 and offset + 1 < len(s)
269 and s[offset + 1 : offset + 2] == b'"'
265 and s[offset + 1 : offset + 2] == b'"'
270 ):
266 ):
271 offset += 1
267 offset += 1
272 parts[-1] += b'"'
268 parts[-1] += b'"'
273 else:
269 else:
274 parts[-1] += s[offset : offset + 1]
270 parts[-1] += s[offset : offset + 1]
275 offset += 1
271 offset += 1
276
272
277 if offset >= len(s):
273 if offset >= len(s):
278 real_parts = _configlist(parts[-1])
274 real_parts = _configlist(parts[-1])
279 if not real_parts:
275 if not real_parts:
280 parts[-1] = b'"'
276 parts[-1] = b'"'
281 else:
277 else:
282 real_parts[0] = b'"' + real_parts[0]
278 real_parts[0] = b'"' + real_parts[0]
283 parts = parts[:-1]
279 parts = parts[:-1]
284 parts.extend(real_parts)
280 parts.extend(real_parts)
285 return None, parts, offset
281 return None, parts, offset
286
282
287 offset += 1
283 offset += 1
288 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
284 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
289 offset += 1
285 offset += 1
290
286
291 if offset < len(s):
287 if offset < len(s):
292 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
288 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
293 parts[-1] += b'"'
289 parts[-1] += b'"'
294 offset += 1
290 offset += 1
295 else:
291 else:
296 parts.append(b'')
292 parts.append(b'')
297 else:
293 else:
298 return None, parts, offset
294 return None, parts, offset
299
295
300 return _parse_plain, parts, offset
296 return _parse_plain, parts, offset
301
297
302 def _configlist(s):
298 def _configlist(s):
303 s = s.rstrip(b' ,')
299 s = s.rstrip(b' ,')
304 if not s:
300 if not s:
305 return []
301 return []
306 parser, parts, offset = _parse_plain, [b''], 0
302 parser, parts, offset = _parse_plain, [b''], 0
307 while parser:
303 while parser:
308 parser, parts, offset = parser(parts, s, offset)
304 parser, parts, offset = parser(parts, s, offset)
309 return parts
305 return parts
310
306
311 if value is not None and isinstance(value, bytes):
307 if value is not None and isinstance(value, bytes):
312 result = _configlist(value.lstrip(b' ,\n'))
308 result = _configlist(value.lstrip(b' ,\n'))
313 else:
309 else:
314 result = value
310 result = value
315 return result or []
311 return result or []
General Comments 0
You need to be logged in to leave comments. Login now