##// END OF EJS Templates
branching: merge default into stable...
branching: merge default into stable This mark the start of the 5.9 freeze.

File last commit:

r47960:b0e92313 default
r48559:d7515d29 merge 5.9rc0 stable
Show More
config.py
260 lines | 8.6 KiB | text/x-python | PythonLexer
Martin Geisler
config: add copyright and license header
r8229 # config.py - configuration parsing for Mercurial
#
Raphaël Gomès
contributor: change mentions of mpm to olivia...
r47575 # Copyright 2009 Olivia Mackall <olivia@selenic.com> and others
Martin Geisler
config: add copyright and license header
r8229 #
# This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Martin Geisler
config: add copyright and license header
r8229
Gregory Szorc
config: use absolute_import
r25923 from __future__ import absolute_import
import errno
import os
from .i18n import _
Gregory Szorc
py3: manually import getattr where it is needed...
r43359 from .pycompat import getattr
Gregory Szorc
config: use absolute_import
r25923 from . import (
Denis Laxalde
py3: encode underlying error message during parse error of %include
r43570 encoding,
Gregory Szorc
config: use absolute_import
r25923 error,
Augie Fackler
config: guard against setconfig specifying unicode values on py3...
r31306 pycompat,
Gregory Szorc
config: use absolute_import
r25923 util,
)
Matt Mackall
ui: introduce new config parser
r8144
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
config: add some helper methods
r8186 class config(object):
Martin von Zweigbergk
config: remove now-unused support for "includepaths"...
r45814 def __init__(self, data=None):
config: track the "level" of a value...
r47367 self._current_source_level = 0
Matt Mackall
ui: introduce new config parser
r8144 self._data = {}
FUJIWARA Katsunori
config: discard "%unset" values defined in the other files read in previously...
r19087 self._unset = []
Matt Mackall
ui: introduce new config parser
r8144 if data:
for k in data._data:
self._data[k] = data[k].copy()
config: track the "level" of a value...
r47367 self._current_source_level = data._current_source_level + 1
def new_source(self):
"""increment the source counter
This is used to define source priority when reading"""
self._current_source_level += 1
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
ui: introduce new config parser
r8144 def copy(self):
return config(self)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
ui: introduce new config parser
r8144 def __contains__(self, section):
return section in self._data
Augie Fackler
formatting: blacken the codebase...
r43346
Bryan O'Sullivan
config: add hasconfig method and supporting plumbing...
r27696 def hasitem(self, section, item):
return item in self._data.get(section, {})
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
config: add some helper methods
r8186 def __getitem__(self, section):
return self._data.get(section, {})
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
config: add some helper methods
r8186 def __iter__(self):
for d in self.sections():
yield d
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
config: add section filter to read...
r8193 def update(self, src):
config: track the "level" of a value...
r47367 current_level = self._current_source_level
current_level += 1
max_level = self._current_source_level
FUJIWARA Katsunori
config: discard "%unset" values defined in the other files read in previously...
r19087 for s, n in src._unset:
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 ds = self._data.get(s, None)
if ds is not None and n in ds:
self._data[s] = ds.preparewrite()
FUJIWARA Katsunori
config: discard "%unset" values defined in the other files read in previously...
r19087 del self._data[s][n]
Matt Mackall
config: add section filter to read...
r8193 for s in src:
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 ds = self._data.get(s, None)
if ds:
self._data[s] = ds.preparewrite()
else:
self._data[s] = util.cowsortdict()
config: track the "level" of a value...
r47367 for k, v in src._data[s].items():
value, source, level = v
level += current_level
max_level = max(level, current_level)
self._data[s][k] = (value, source, level)
self._current_source_level = max_level
config: track "source" along side value...
r47366
def _get(self, section, item):
return self._data.get(section, {}).get(item)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
ui: introduce new config parser
r8144 def get(self, section, item, default=None):
config: track "source" along side value...
r47366 result = self._get(section, item)
if result is None:
return default
return result[0]
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919
config: track "source" along side value...
r47366 def backup(self, section, key):
timeless@mozdev.org
spelling: value
r17527 """return a tuple allowing restore to reinstall a previous value
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919
timeless@mozdev.org
grammar: it-handles
r17530 The main reason we need it is because it handles the "no data" case.
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 """
try:
config: track "source" along side value...
r47366 item = self._data[section][key]
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 except KeyError:
config: track "source" along side value...
r47366 return (section, key)
else:
return (section, key) + item
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919
Matt Mackall
config: getsource -> source
r8198 def source(self, section, item):
config: track "source" along side value...
r47366 result = self._get(section, item)
if result is None:
return b""
return result[1]
Augie Fackler
formatting: blacken the codebase...
r43346
config: track the "level" of a value...
r47367 def level(self, section, item):
result = self._get(section, item)
if result is None:
return None
return result[2]
Matt Mackall
ui: introduce new config parser
r8144 def sections(self):
return sorted(self._data.keys())
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
ui: introduce new config parser
r8144 def items(self, section):
config: track "source" along side value...
r47366 items = pycompat.iteritems(self._data.get(section, {}))
config: track the "level" of a value...
r47367 return [(k, v[0]) for (k, v) in items]
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def set(self, section, item, value, source=b""):
Augie Fackler
config: guard against setconfig specifying unicode values on py3...
r31306 if pycompat.ispy3:
Augie Fackler
formatting: blacken the codebase...
r43346 assert not isinstance(
section, str
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ), b'config section may not be unicode strings on Python 3'
Augie Fackler
formatting: blacken the codebase...
r43346 assert not isinstance(
item, str
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ), b'config item may not be unicode strings on Python 3'
Augie Fackler
formatting: blacken the codebase...
r43346 assert not isinstance(
value, str
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ), b'config values may not be unicode strings on Python 3'
Matt Mackall
ui: introduce new config parser
r8144 if section not in self:
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._data[section] = util.cowsortdict()
else:
self._data[section] = self._data[section].preparewrite()
config: track the "level" of a value...
r47367 self._data[section][item] = (value, source, self._current_source_level)
Matt Mackall
ui: introduce new config parser
r8144
config: use a new `alter` method in `fixconfig`...
r47365 def alter(self, section, key, new_value):
"""alter a value without altering its source or level
This method is meant to be used by `ui.fixconfig` only."""
item = self._data[section][key]
size = len(item)
new_item = (new_value,) + item[1:]
assert len(new_item) == size
self._data[section][key] = new_item
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 def restore(self, data):
"""restore data returned by self.backup"""
config: track "source" along side value...
r47366 if len(data) != 2:
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 # restore old data
config: track "source" along side value...
r47366 section, key = data[:2]
item = data[2:]
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._data[section] = self._data[section].preparewrite()
config: track "source" along side value...
r47366 self._data[section][key] = item
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 else:
# no data before, remove everything
section, item = data
if section in self._data:
Pierre-Yves David
config: fix restoreconfig of non existing config...
r22037 self._data[section].pop(item, None)
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919
Matt Mackall
config: add parse interface
r8265 def parse(self, src, data, sections=None, remap=None, include=None):
Augie Fackler
config: mark parser regexes as bytes explicitly...
r30349 sectionre = util.re.compile(br'\[([^\[]+)\]')
itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
emptyre = util.re.compile(br'(;|#|\s*$)')
commentre = util.re.compile(br'(;|#)')
unsetre = util.re.compile(br'%unset\s+(\S+)')
includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 section = b""
Matt Mackall
ui: introduce new config parser
r8144 item = None
line = 0
config: improve code readability
r9339 cont = False
Matt Mackall
hgweb: use config.config
r8180
Yuya Nishihara
config: allow remapping the default section...
r34714 if remap:
section = remap.get(section, section)
Nicolas Dumazet
for calls expecting bool args, pass bool instead of int...
r9136 for l in data.splitlines(True):
Matt Mackall
ui: introduce new config parser
r8144 line += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if line == 1 and l.startswith(b'\xef\xbb\xbf'):
Matt Mackall
config: discard UTF-8 BOM if found
r16348 # Someone set us up the BOM
l = l[3:]
Matt Mackall
ui: introduce new config parser
r8144 if cont:
Matt Mackall
config: handle comment lines in continuations (issue2854)
r14642 if commentre.match(l):
continue
Matt Mackall
ui: introduce new config parser
r8144 m = contre.match(l)
if m:
Matt Mackall
config: add section filter to read...
r8193 if sections and section not in sections:
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 v = self.get(section, item) + b"\n" + m.group(1)
self.set(section, item, v, b"%s:%d" % (src, line))
Matt Mackall
ui: introduce new config parser
r8144 continue
item = None
Nicolas Dumazet
config: abort on indented non-continuation lines (issue1829)...
r9469 cont = False
Matt Mackall
config: allow including other config files
r8183 m = includere.match(l)
Jordi Gutiérrez Hermoso
config: give it an includepaths option for looking for config files...
r25095
if m and include:
expanded = util.expandpath(m.group(1))
Martin von Zweigbergk
config: remove now-unused support for "includepaths"...
r45814 try:
Martin von Zweigbergk
config: remove now-unused `abs` argument from `include` callback...
r45817 include(expanded, remap=remap, sections=sections)
Martin von Zweigbergk
config: remove now-unused support for "includepaths"...
r45814 except IOError as inst:
if inst.errno != errno.ENOENT:
Martin von Zweigbergk
errors: raise ConfigError on failure to parse config file...
r46506 raise error.ConfigError(
Martin von Zweigbergk
config: remove now-unused support for "includepaths"...
r45814 _(b"cannot include %s (%s)")
Martin von Zweigbergk
config: remove now-unused `abs` argument from `include` callback...
r45817 % (expanded, encoding.strtolocal(inst.strerror)),
Martin von Zweigbergk
config: remove now-unused support for "includepaths"...
r45814 b"%s:%d" % (src, line),
)
Matt Mackall
config: allow including other config files
r8183 continue
Matt Mackall
ui: introduce new config parser
r8144 if emptyre.match(l):
continue
m = sectionre.match(l)
if m:
section = m.group(1)
Matt Mackall
config: make remap actually work
r8298 if remap:
section = remap.get(section, section)
Matt Mackall
ui: introduce new config parser
r8144 if section not in self:
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._data[section] = util.cowsortdict()
Matt Mackall
ui: introduce new config parser
r8144 continue
m = itemre.match(l)
if m:
item = m.group(1)
config: improve code readability
r9339 cont = True
Matt Mackall
config: add section filter to read...
r8193 if sections and section not in sections:
continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
Matt Mackall
ui: introduce new config parser
r8144 continue
Matt Mackall
config: add %unset name support
r8184 m = unsetre.match(l)
if m:
name = m.group(1)
Matt Mackall
config: add section filter to read...
r8193 if sections and section not in sections:
continue
Martin Geisler
code style: prefer 'is' and 'is not' tests with singletons
r13031 if self.get(section, name) is not None:
Jun Wu
config: add a missing preparewrite() call...
r34455 self._data[section] = self._data[section].preparewrite()
Matt Mackall
config: add %unset name support
r8184 del self._data[section][name]
FUJIWARA Katsunori
config: discard "%unset" values defined in the other files read in previously...
r19087 self._unset.append((section, name))
Matt Mackall
config: add %unset name support
r8184 continue
Martin von Zweigbergk
config: move message about leading spaces in config to config.py...
r46362 message = l.rstrip()
if l.startswith(b' '):
message = b"unexpected leading whitespace: %s" % message
Martin von Zweigbergk
errors: raise ConfigError on failure to parse config file...
r46506 raise error.ConfigError(message, (b"%s:%d" % (src, line)))
Matt Mackall
config: add parse interface
r8265
def read(self, path, fp=None, sections=None, remap=None):
config: track the "level" of a value...
r47367 self.new_source()
Matt Mackall
config: add parse interface
r8265 if not fp:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp = util.posixfile(path, b'rb')
Augie Fackler
formating: upgrade to black 20.8b1...
r46554 assert (
getattr(fp, 'mode', 'rb') == 'rb'
), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
fp,
fp.mode,
Augie Fackler
formatting: blacken the codebase...
r43346 )
Martin von Zweigbergk
config: pass both relative and absolute paths to `include` callback...
r45768
Martin von Zweigbergk
config: re-calculate absolute %include path in `include` callback...
r45816 dir = os.path.dirname(path)
Martin von Zweigbergk
config: remove now-unused `abs` argument from `include` callback...
r45817 def include(rel, remap, sections):
Martin von Zweigbergk
config: re-calculate absolute %include path in `include` callback...
r45816 abs = os.path.normpath(os.path.join(dir, rel))
Martin von Zweigbergk
config: pass both relative and absolute paths to `include` callback...
r45768 self.read(abs, remap=remap, sections=sections)
config: track the "level" of a value...
r47367 # anything after the include has a higher level
self.new_source()
Martin von Zweigbergk
config: pass both relative and absolute paths to `include` callback...
r45768
Augie Fackler
formatting: blacken the codebase...
r43346 self.parse(
Martin von Zweigbergk
config: pass both relative and absolute paths to `include` callback...
r45768 path, fp.read(), sections=sections, remap=remap, include=include
Augie Fackler
formatting: blacken the codebase...
r43346 )