##// END OF EJS Templates
pytype: use "$(hg root)" instead of `hg root` to make shellcheck happier
pytype: use "$(hg root)" instead of `hg root` to make shellcheck happier

File last commit:

r52182:7bd7fcc7 default
r52199:46280260 default
Show More
config.py
261 lines | 8.5 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
import errno
import os
pytype: drop the last inline type comment...
r52182 from typing import (
List,
Tuple,
)
Gregory Szorc
config: use absolute_import
r25923 from .i18n import _
from . import (
Denis Laxalde
py3: encode underlying error message during parse error of %include
r43570 encoding,
Gregory Szorc
config: use absolute_import
r25923 error,
util,
)
Matt Mackall
ui: introduce new config parser
r8144
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class config:
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
pytype: drop the last inline type comment...
r52182 def items(self, section: bytes) -> List[Tuple[bytes, bytes]]:
Gregory Szorc
config: remove pycompat.iteritems()...
r49775 items = self._data.get(section, {}).items()
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""):
Gregory Szorc
config: remove conditional asserts...
r49745 assert not isinstance(
section, str
), b'config section may not be unicode strings on Python 3'
assert not isinstance(
item, str
), b'config item may not be unicode strings on Python 3'
assert not isinstance(
value, str
), 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 )