20 ) |
20 ) |
21 |
21 |
22 |
22 |
23 class config(object): |
23 class config(object): |
24 def __init__(self, data=None): |
24 def __init__(self, data=None): |
|
25 self._current_source_level = 0 |
25 self._data = {} |
26 self._data = {} |
26 self._unset = [] |
27 self._unset = [] |
27 if data: |
28 if data: |
28 for k in data._data: |
29 for k in data._data: |
29 self._data[k] = data[k].copy() |
30 self._data[k] = data[k].copy() |
|
31 self._current_source_level = data._current_source_level + 1 |
|
32 |
|
33 def new_source(self): |
|
34 """increment the source counter |
|
35 |
|
36 This is used to define source priority when reading""" |
|
37 self._current_source_level += 1 |
30 |
38 |
31 def copy(self): |
39 def copy(self): |
32 return config(self) |
40 return config(self) |
33 |
41 |
34 def __contains__(self, section): |
42 def __contains__(self, section): |
43 def __iter__(self): |
51 def __iter__(self): |
44 for d in self.sections(): |
52 for d in self.sections(): |
45 yield d |
53 yield d |
46 |
54 |
47 def update(self, src): |
55 def update(self, src): |
|
56 current_level = self._current_source_level |
|
57 current_level += 1 |
|
58 max_level = self._current_source_level |
48 for s, n in src._unset: |
59 for s, n in src._unset: |
49 ds = self._data.get(s, None) |
60 ds = self._data.get(s, None) |
50 if ds is not None and n in ds: |
61 if ds is not None and n in ds: |
51 self._data[s] = ds.preparewrite() |
62 self._data[s] = ds.preparewrite() |
52 del self._data[s][n] |
63 del self._data[s][n] |
54 ds = self._data.get(s, None) |
65 ds = self._data.get(s, None) |
55 if ds: |
66 if ds: |
56 self._data[s] = ds.preparewrite() |
67 self._data[s] = ds.preparewrite() |
57 else: |
68 else: |
58 self._data[s] = util.cowsortdict() |
69 self._data[s] = util.cowsortdict() |
59 self._data[s].update(src._data[s]) |
70 for k, v in src._data[s].items(): |
|
71 value, source, level = v |
|
72 level += current_level |
|
73 max_level = max(level, current_level) |
|
74 self._data[s][k] = (value, source, level) |
|
75 self._current_source_level = max_level |
60 |
76 |
61 def _get(self, section, item): |
77 def _get(self, section, item): |
62 return self._data.get(section, {}).get(item) |
78 return self._data.get(section, {}).get(item) |
63 |
79 |
64 def get(self, section, item, default=None): |
80 def get(self, section, item, default=None): |
83 result = self._get(section, item) |
99 result = self._get(section, item) |
84 if result is None: |
100 if result is None: |
85 return b"" |
101 return b"" |
86 return result[1] |
102 return result[1] |
87 |
103 |
|
104 def level(self, section, item): |
|
105 result = self._get(section, item) |
|
106 if result is None: |
|
107 return None |
|
108 return result[2] |
|
109 |
88 def sections(self): |
110 def sections(self): |
89 return sorted(self._data.keys()) |
111 return sorted(self._data.keys()) |
90 |
112 |
91 def items(self, section): |
113 def items(self, section): |
92 items = pycompat.iteritems(self._data.get(section, {})) |
114 items = pycompat.iteritems(self._data.get(section, {})) |
93 return [(k, v) for (k, (v, s)) in items] |
115 return [(k, v[0]) for (k, v) in items] |
94 |
116 |
95 def set(self, section, item, value, source=b""): |
117 def set(self, section, item, value, source=b""): |
96 if pycompat.ispy3: |
118 if pycompat.ispy3: |
97 assert not isinstance( |
119 assert not isinstance( |
98 section, str |
120 section, str |
105 ), b'config values may not be unicode strings on Python 3' |
127 ), b'config values may not be unicode strings on Python 3' |
106 if section not in self: |
128 if section not in self: |
107 self._data[section] = util.cowsortdict() |
129 self._data[section] = util.cowsortdict() |
108 else: |
130 else: |
109 self._data[section] = self._data[section].preparewrite() |
131 self._data[section] = self._data[section].preparewrite() |
110 self._data[section][item] = (value, source) |
132 self._data[section][item] = (value, source, self._current_source_level) |
111 |
133 |
112 def alter(self, section, key, new_value): |
134 def alter(self, section, key, new_value): |
113 """alter a value without altering its source or level |
135 """alter a value without altering its source or level |
114 |
136 |
115 This method is meant to be used by `ui.fixconfig` only.""" |
137 This method is meant to be used by `ui.fixconfig` only.""" |
213 if l.startswith(b' '): |
235 if l.startswith(b' '): |
214 message = b"unexpected leading whitespace: %s" % message |
236 message = b"unexpected leading whitespace: %s" % message |
215 raise error.ConfigError(message, (b"%s:%d" % (src, line))) |
237 raise error.ConfigError(message, (b"%s:%d" % (src, line))) |
216 |
238 |
217 def read(self, path, fp=None, sections=None, remap=None): |
239 def read(self, path, fp=None, sections=None, remap=None): |
|
240 self.new_source() |
218 if not fp: |
241 if not fp: |
219 fp = util.posixfile(path, b'rb') |
242 fp = util.posixfile(path, b'rb') |
220 assert ( |
243 assert ( |
221 getattr(fp, 'mode', 'rb') == 'rb' |
244 getattr(fp, 'mode', 'rb') == 'rb' |
222 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % ( |
245 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % ( |
227 dir = os.path.dirname(path) |
250 dir = os.path.dirname(path) |
228 |
251 |
229 def include(rel, remap, sections): |
252 def include(rel, remap, sections): |
230 abs = os.path.normpath(os.path.join(dir, rel)) |
253 abs = os.path.normpath(os.path.join(dir, rel)) |
231 self.read(abs, remap=remap, sections=sections) |
254 self.read(abs, remap=remap, sections=sections) |
|
255 # anything after the include has a higher level |
|
256 self.new_source() |
232 |
257 |
233 self.parse( |
258 self.parse( |
234 path, fp.read(), sections=sections, remap=remap, include=include |
259 path, fp.read(), sections=sections, remap=remap, include=include |
235 ) |
260 ) |
236 |
261 |