comparison mercurial/config.py @ 46622:a3dced4b7b04

config: track the "level" of a value Config value now remember the "level" of the config that loaded it. This will be used to ensure good priority management for alias. Differential Revision: https://phab.mercurial-scm.org/D9926
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Fri, 29 Jan 2021 11:21:49 +0100
parents d3df397e7a59
children d4ba4d51f85f
comparison
equal deleted inserted replaced
46621:d3df397e7a59 46622:a3dced4b7b04
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