[PATCH 1 of 2] config: introduce load order tracking
Boris Feld
boris.feld at octobus.net
Mon Jul 9 10:12:21 UTC 2018
# HG changeset patch
# User Boris Feld <boris.feld at octobus.net>
# Date 1530887625 -7200
# Fri Jul 06 16:33:45 2018 +0200
# Node ID 1019d8a4f6b810aaa63651ed56b29668650f590e
# Parent 8c38d29482177cd40243a008057d6762c7d23c6f
# EXP-Topic config-order
# Available At https://bitbucket.org/octobus/mercurial-devel/
# hg pull https://bitbucket.org/octobus/mercurial-devel/ -r 1019d8a4f6b8
config: introduce load order tracking
Configuration values reads from disk are now associated with the index of the
file they came from. (First loaded file has index 0, second file index 1,
etcâ¦).
This will ultimately allow us to use the alias value of a configuration item
if it was defined in a higher priority file than the configuration item value
itself.
See next changeset for details.
Value set directly through the code or through the command line have the highest
priority.
diff --git a/mercurial/config.py b/mercurial/config.py
--- a/mercurial/config.py
+++ b/mercurial/config.py
@@ -26,8 +26,11 @@ class config(object):
for k in data._data:
self._data[k] = data[k].copy()
self._source = data._source.copy()
+ self._level = data._level.copy()
else:
self._source = util.cowdict()
+ self._level = util.cowdict()
+
def copy(self):
return config(self)
def __contains__(self, section):
@@ -47,6 +50,7 @@ class config(object):
self._data[s] = ds.preparewrite()
del self._data[s][n]
del self._source[(s, n)]
+ self._level.pop((s, n), None)
for s in src:
ds = self._data.get(s, None)
if ds:
@@ -55,6 +59,7 @@ class config(object):
self._data[s] = util.cowsortdict()
self._data[s].update(src._data[s])
self._source.update(src._source)
+ self._level.update(src._level)
def get(self, section, item, default=None):
return self._data.get(section, {}).get(item, default)
@@ -66,17 +71,20 @@ class config(object):
try:
value = self._data[section][item]
source = self.source(section, item)
- return (section, item, value, source)
+ level = self.level(section, item)
+ return (section, item, value, source, level)
except KeyError:
return (section, item)
def source(self, section, item):
return self._source.get((section, item), "")
+ def level(self, section, item):
+ return self._level.get((section, item), None)
def sections(self):
return sorted(self._data.keys())
def items(self, section):
return list(self._data.get(section, {}).iteritems())
- def set(self, section, item, value, source=""):
+ def set(self, section, item, value, source="", level=None):
if pycompat.ispy3:
assert not isinstance(value, str), (
'config values may not be unicode strings on Python 3')
@@ -88,24 +96,30 @@ class config(object):
if source:
self._source = self._source.preparewrite()
self._source[(section, item)] = source
+ if level is not None:
+ self._level[(section, item)] = level
def restore(self, data):
"""restore data returned by self.backup"""
self._source = self._source.preparewrite()
- if len(data) == 4:
+ self._level = self._level.preparewrite()
+ if len(data) == 5:
# restore old data
- section, item, value, source = data
+ section, item, value, source, level = data
self._data[section] = self._data[section].preparewrite()
self._data[section][item] = value
self._source[(section, item)] = source
+ self._source[(section, item)] = level
else:
# no data before, remove everything
section, item = data
if section in self._data:
self._data[section].pop(item, None)
self._source.pop((section, item), None)
+ self._level.pop((section, item), None)
- def parse(self, src, data, sections=None, remap=None, include=None):
+ def parse(self, src, data, sections=None, remap=None, include=None,
+ level=None):
sectionre = util.re.compile(br'\[([^\[]+)\]')
itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
@@ -134,7 +148,8 @@ class config(object):
if sections and section not in sections:
continue
v = self.get(section, item) + "\n" + m.group(1)
- self.set(section, item, v, "%s:%d" % (src, line))
+ self.set(section, item, v, "%s:%d" % (src, line),
+ level=level)
continue
item = None
cont = False
@@ -172,7 +187,8 @@ class config(object):
cont = True
if sections and section not in sections:
continue
- self.set(section, item, m.group(2), "%s:%d" % (src, line))
+ self.set(section, item, m.group(2), "%s:%d" % (src, line),
+ level=level)
continue
m = unsetre.match(l)
if m:
@@ -187,14 +203,14 @@ class config(object):
raise error.ParseError(l.rstrip(), ("%s:%d" % (src, line)))
- def read(self, path, fp=None, sections=None, remap=None):
+ def read(self, path, fp=None, sections=None, remap=None, level=None):
if not fp:
fp = util.posixfile(path, 'rb')
assert getattr(fp, 'mode', r'rb') == r'rb', (
'config files must be opened in binary mode, got fp=%r mode=%r' % (
fp, fp.mode))
- self.parse(path, fp.read(),
- sections=sections, remap=remap, include=self.read)
+ self.parse(path, fp.read(), sections=sections, remap=remap,
+ include=self.read, level=level)
def parselist(value):
"""parse a configuration value as a list of comma/space separated strings
diff --git a/mercurial/ui.py b/mercurial/ui.py
--- a/mercurial/ui.py
+++ b/mercurial/ui.py
@@ -191,6 +191,9 @@ def _catchterm(*args):
# _reqexithandlers: callbacks run at the end of a request
_reqexithandlers = []
+# config level set directly have higher level than those from disk
+directconfig = sys.maxint
+
class ui(object):
def __init__(self, src=None):
"""Create a fresh new ui object if no src given
@@ -210,6 +213,7 @@ class ui(object):
self.quiet = self.verbose = self.debugflag = self.tracebackflag = False
self._reportuntrusted = True
self._knownconfig = configitems.coreitems
+ self._configlevel = 0
self._ocfg = config.config() # overlay
self._tcfg = config.config() # trusted
self._ucfg = config.config() # untrusted
@@ -234,6 +238,7 @@ class ui(object):
self._disablepager = src._disablepager
self._tweaked = src._tweaked
+ self._configlevel = src._configlevel
self._tcfg = src._tcfg.copy()
self._ucfg = src._ucfg.copy()
self._ocfg = src._ocfg.copy()
@@ -282,12 +287,14 @@ class ui(object):
if t == 'path':
u.readconfig(f, trust=True)
elif t == 'items':
+ level = u._configlevel
+ u._configlevel += 1
sections = set()
for section, name, value, source in f:
# do not set u._ocfg
# XXX clean this up once immutable config object is a thing
- u._tcfg.set(section, name, value, source)
- u._ucfg.set(section, name, value, source)
+ u._tcfg.set(section, name, value, source, level=level)
+ u._ucfg.set(section, name, value, source, level=level)
sections.add(section)
for section in sections:
u.fixconfig(section=section)
@@ -314,7 +321,9 @@ class ui(object):
for section in tmpcfg:
for name, value in tmpcfg.items(section):
if not self.hasconfig(section, name):
- self.setconfig(section, name, value, "<tweakdefaults>")
+ self.setconfig(section, name, value, "<tweakdefaults>",
+ level=self._configlevel)
+ self._configlevel += 1
def copy(self):
return self.__class__(self)
@@ -390,6 +399,8 @@ class ui(object):
def readconfig(self, filename, root=None, trust=False,
sections=None, remap=None):
+ level = self._configlevel
+ self._configlevel += 1
try:
fp = open(filename, u'rb')
except IOError:
@@ -401,7 +412,7 @@ class ui(object):
trusted = sections or trust or self._trusted(fp, filename)
try:
- cfg.read(filename, fp, sections=sections, remap=remap)
+ cfg.read(filename, fp, sections=sections, remap=remap, level=level)
fp.close()
except error.ConfigError as inst:
if trusted:
@@ -459,7 +470,7 @@ class ui(object):
p = util.expandpath(p)
if not util.hasscheme(p) and not os.path.isabs(p):
p = os.path.normpath(os.path.join(root, p))
- c.set("paths", n, p)
+ c.set("paths", n, p, level=c.level("paths", n))
if section in (None, 'ui'):
# update ui options
@@ -487,9 +498,9 @@ class ui(object):
self._tcfg.restore(data[1])
self._ucfg.restore(data[2])
- def setconfig(self, section, name, value, source=''):
+ def setconfig(self, section, name, value, source='', level=directconfig):
for cfg in (self._ocfg, self._tcfg, self._ucfg):
- cfg.set(section, name, value, source)
+ cfg.set(section, name, value, source, level=level)
self.fixconfig(section=section)
self._maybetweakdefaults()
More information about the Mercurial-devel
mailing list