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