##// END OF EJS Templates
py3: keep stdout as defined by pycompat in procutil...
py3: keep stdout as defined by pycompat in procutil According to https://docs.python.org/3/library/functions.html#open, it's not possible to use 1 as buffering argument value in binary mode. This is probably why line buffering does not work well in python3. On the other hand, sys.stdout.buffer appears to be line-buffered already on python3. So by not replacing it, there should be no behavior change. This fixes buffering issue in "hg email" (confirmation prompt shown before information to be confirmed).

File last commit:

r43376:d783f945 default
r43435:227ba1af default
Show More
config.py
312 lines | 10.4 KiB | text/x-python | PythonLexer
Martin Geisler
config: add copyright and license header
r8229 # config.py - configuration parsing for Mercurial
#
# Copyright 2009 Matt Mackall <mpm@selenic.com> and others
#
# 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 (
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):
Martijn Pieters
config: avoid using a mutable default...
r31374 def __init__(self, data=None, includepaths=None):
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 = []
Martijn Pieters
config: avoid using a mutable default...
r31374 self._includepaths = includepaths or []
Matt Mackall
ui: introduce new config parser
r8144 if data:
for k in data._data:
self._data[k] = data[k].copy()
Matt Mackall
config: split source data out into separate map
r8185 self._source = data._source.copy()
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 else:
self._source = util.cowdict()
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):
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._source = self._source.preparewrite()
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]
del self._source[(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()
Matt Mackall
config: add section filter to read...
r8193 self._data[s].update(src._data[s])
self._source.update(src._source)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
ui: introduce new config parser
r8144 def get(self, section, item, default=None):
Matt Mackall
config: split source data out into separate map
r8185 return self._data.get(section, {}).get(item, default)
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919
def backup(self, section, item):
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:
value = self._data[section][item]
source = self.source(section, item)
return (section, item, value, source)
except KeyError:
return (section, item)
Matt Mackall
config: getsource -> source
r8198 def source(self, section, item):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return self._source.get((section, item), b"")
Augie Fackler
formatting: blacken the codebase...
r43346
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):
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 return list(pycompat.iteritems(self._data.get(section, {})))
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()
Matt Mackall
config: split source data out into separate map
r8185 self._data[section][item] = value
Mads Kiilerich
config: don't set source when no source is specified - don't overwrite with ''...
r20789 if source:
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._source = self._source.preparewrite()
Mads Kiilerich
config: don't set source when no source is specified - don't overwrite with ''...
r20789 self._source[(section, item)] = source
Matt Mackall
ui: introduce new config parser
r8144
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"""
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._source = self._source.preparewrite()
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 if len(data) == 4:
# restore old data
section, item, value, source = data
Jun Wu
config: use copy-on-write to improve copy performance...
r34353 self._data[section] = self._data[section].preparewrite()
Pierre-Yves David
config: have a way to backup and restore value in config...
r15919 self._data[section][item] = value
self._source[(section, item)] = source
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 self._source.pop((section, item), None)
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))
includepaths = [os.path.dirname(src)] + self._includepaths
for base in includepaths:
inc = os.path.normpath(os.path.join(base, expanded))
Martin Geisler
config: raise ConfigError on non-existing include files...
r10042 try:
include(inc, remap=remap, sections=sections)
Jordi Gutiérrez Hermoso
config: give it an includepaths option for looking for config files...
r25095 break
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Matt Mackall
config: ignore include errors for nonexistent files
r14486 if inst.errno != errno.ENOENT:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"cannot include %s (%s)")
Augie Fackler
formatting: blacken the codebase...
r43346 % (inc, inst.strerror),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"%s:%d" % (src, line),
Augie Fackler
formatting: blacken the codebase...
r43346 )
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
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(l.rstrip(), (b"%s:%d" % (src, line)))
Matt Mackall
config: add parse interface
r8265
def read(self, path, fp=None, sections=None, remap=None):
if not fp:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fp = util.posixfile(path, b'rb')
Augie Fackler
formatting: blacken the codebase...
r43346 assert (
getattr(fp, 'mode', r'rb') == r'rb'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
Augie Fackler
formatting: blacken the codebase...
r43346 fp,
fp.mode,
)
self.parse(
path, fp.read(), sections=sections, remap=remap, include=self.read
)
Jun Wu
ui: move configlist parser to config.py...
r31481
def parselist(value):
"""parse a configuration value as a list of comma/space separated strings
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> parselist(b'this,is "a small" ,test')
Jun Wu
ui: move configlist parser to config.py...
r31481 ['this', 'is', 'a small', 'test']
"""
def _parse_plain(parts, s, offset):
whitespace = False
Augie Fackler
formatting: blacken the codebase...
r43346 while offset < len(s) and (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Jun Wu
ui: move configlist parser to config.py...
r31481 whitespace = True
offset += 1
if offset >= len(s):
return None, parts, offset
if whitespace:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parts.append(b'')
if s[offset : offset + 1] == b'"' and not parts[-1]:
Jun Wu
ui: move configlist parser to config.py...
r31481 return _parse_quote, parts, offset + 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
Augie Fackler
formatting: blacken the codebase...
r43346 parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
Jun Wu
ui: move configlist parser to config.py...
r31481 return _parse_plain, parts, offset + 1
Augie Fackler
formatting: blacken the codebase...
r43346 parts[-1] += s[offset : offset + 1]
Jun Wu
ui: move configlist parser to config.py...
r31481 return _parse_plain, parts, offset + 1
def _parse_quote(parts, s, offset):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if offset < len(s) and s[offset : offset + 1] == b'"': # ""
parts.append(b'')
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
Augie Fackler
formatting: blacken the codebase...
r43346 while offset < len(s) and (
s[offset : offset + 1].isspace()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 or s[offset : offset + 1] == b','
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
return _parse_plain, parts, offset
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 while offset < len(s) and s[offset : offset + 1] != b'"':
Augie Fackler
formatting: blacken the codebase...
r43346 if (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s[offset : offset + 1] == b'\\'
Augie Fackler
formatting: blacken the codebase...
r43346 and offset + 1 < len(s)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 and s[offset + 1 : offset + 2] == b'"'
Augie Fackler
formatting: blacken the codebase...
r43346 ):
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parts[-1] += b'"'
Jun Wu
ui: move configlist parser to config.py...
r31481 else:
Augie Fackler
formatting: blacken the codebase...
r43346 parts[-1] += s[offset : offset + 1]
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
if offset >= len(s):
real_parts = _configlist(parts[-1])
if not real_parts:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parts[-1] = b'"'
Jun Wu
ui: move configlist parser to config.py...
r31481 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 real_parts[0] = b'"' + real_parts[0]
Jun Wu
ui: move configlist parser to config.py...
r31481 parts = parts[:-1]
parts.extend(real_parts)
return None, parts, offset
offset += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
if offset < len(s):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
parts[-1] += b'"'
Jun Wu
ui: move configlist parser to config.py...
r31481 offset += 1
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parts.append(b'')
Jun Wu
ui: move configlist parser to config.py...
r31481 else:
return None, parts, offset
return _parse_plain, parts, offset
def _configlist(s):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 s = s.rstrip(b' ,')
Jun Wu
ui: move configlist parser to config.py...
r31481 if not s:
return []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parser, parts, offset = _parse_plain, [b''], 0
Jun Wu
ui: move configlist parser to config.py...
r31481 while parser:
parser, parts, offset = parser(parts, s, offset)
return parts
if value is not None and isinstance(value, bytes):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 result = _configlist(value.lstrip(b' ,\n'))
Jun Wu
ui: move configlist parser to config.py...
r31481 else:
result = value
return result or []