mercurial/config.py
changeset 43076 2372284d9457
parent 41317 4ad002b2584d
child 43077 687b865b95ad
equal deleted inserted replaced
43075:57875cf423c9 43076:2372284d9457
    14 from . import (
    14 from . import (
    15     error,
    15     error,
    16     pycompat,
    16     pycompat,
    17     util,
    17     util,
    18 )
    18 )
       
    19 
    19 
    20 
    20 class config(object):
    21 class config(object):
    21     def __init__(self, data=None, includepaths=None):
    22     def __init__(self, data=None, includepaths=None):
    22         self._data = {}
    23         self._data = {}
    23         self._unset = []
    24         self._unset = []
    26             for k in data._data:
    27             for k in data._data:
    27                 self._data[k] = data[k].copy()
    28                 self._data[k] = data[k].copy()
    28             self._source = data._source.copy()
    29             self._source = data._source.copy()
    29         else:
    30         else:
    30             self._source = util.cowdict()
    31             self._source = util.cowdict()
       
    32 
    31     def copy(self):
    33     def copy(self):
    32         return config(self)
    34         return config(self)
       
    35 
    33     def __contains__(self, section):
    36     def __contains__(self, section):
    34         return section in self._data
    37         return section in self._data
       
    38 
    35     def hasitem(self, section, item):
    39     def hasitem(self, section, item):
    36         return item in self._data.get(section, {})
    40         return item in self._data.get(section, {})
       
    41 
    37     def __getitem__(self, section):
    42     def __getitem__(self, section):
    38         return self._data.get(section, {})
    43         return self._data.get(section, {})
       
    44 
    39     def __iter__(self):
    45     def __iter__(self):
    40         for d in self.sections():
    46         for d in self.sections():
    41             yield d
    47             yield d
       
    48 
    42     def update(self, src):
    49     def update(self, src):
    43         self._source = self._source.preparewrite()
    50         self._source = self._source.preparewrite()
    44         for s, n in src._unset:
    51         for s, n in src._unset:
    45             ds = self._data.get(s, None)
    52             ds = self._data.get(s, None)
    46             if ds is not None and n in ds:
    53             if ds is not None and n in ds:
    53                 self._data[s] = ds.preparewrite()
    60                 self._data[s] = ds.preparewrite()
    54             else:
    61             else:
    55                 self._data[s] = util.cowsortdict()
    62                 self._data[s] = util.cowsortdict()
    56             self._data[s].update(src._data[s])
    63             self._data[s].update(src._data[s])
    57         self._source.update(src._source)
    64         self._source.update(src._source)
       
    65 
    58     def get(self, section, item, default=None):
    66     def get(self, section, item, default=None):
    59         return self._data.get(section, {}).get(item, default)
    67         return self._data.get(section, {}).get(item, default)
    60 
    68 
    61     def backup(self, section, item):
    69     def backup(self, section, item):
    62         """return a tuple allowing restore to reinstall a previous value
    70         """return a tuple allowing restore to reinstall a previous value
    70         except KeyError:
    78         except KeyError:
    71             return (section, item)
    79             return (section, item)
    72 
    80 
    73     def source(self, section, item):
    81     def source(self, section, item):
    74         return self._source.get((section, item), "")
    82         return self._source.get((section, item), "")
       
    83 
    75     def sections(self):
    84     def sections(self):
    76         return sorted(self._data.keys())
    85         return sorted(self._data.keys())
       
    86 
    77     def items(self, section):
    87     def items(self, section):
    78         return list(self._data.get(section, {}).iteritems())
    88         return list(self._data.get(section, {}).iteritems())
       
    89 
    79     def set(self, section, item, value, source=""):
    90     def set(self, section, item, value, source=""):
    80         if pycompat.ispy3:
    91         if pycompat.ispy3:
    81             assert not isinstance(section, str), (
    92             assert not isinstance(
    82                 'config section may not be unicode strings on Python 3')
    93                 section, str
    83             assert not isinstance(item, str), (
    94             ), 'config section may not be unicode strings on Python 3'
    84                 'config item may not be unicode strings on Python 3')
    95             assert not isinstance(
    85             assert not isinstance(value, str), (
    96                 item, str
    86                 'config values may not be unicode strings on Python 3')
    97             ), 'config item may not be unicode strings on Python 3'
       
    98             assert not isinstance(
       
    99                 value, str
       
   100             ), 'config values may not be unicode strings on Python 3'
    87         if section not in self:
   101         if section not in self:
    88             self._data[section] = util.cowsortdict()
   102             self._data[section] = util.cowsortdict()
    89         else:
   103         else:
    90             self._data[section] = self._data[section].preparewrite()
   104             self._data[section] = self._data[section].preparewrite()
    91         self._data[section][item] = value
   105         self._data[section][item] = value
   154                     try:
   168                     try:
   155                         include(inc, remap=remap, sections=sections)
   169                         include(inc, remap=remap, sections=sections)
   156                         break
   170                         break
   157                     except IOError as inst:
   171                     except IOError as inst:
   158                         if inst.errno != errno.ENOENT:
   172                         if inst.errno != errno.ENOENT:
   159                             raise error.ParseError(_("cannot include %s (%s)")
   173                             raise error.ParseError(
   160                                                    % (inc, inst.strerror),
   174                                 _("cannot include %s (%s)")
   161                                                    "%s:%d" % (src, line))
   175                                 % (inc, inst.strerror),
       
   176                                 "%s:%d" % (src, line),
       
   177                             )
   162                 continue
   178                 continue
   163             if emptyre.match(l):
   179             if emptyre.match(l):
   164                 continue
   180                 continue
   165             m = sectionre.match(l)
   181             m = sectionre.match(l)
   166             if m:
   182             if m:
   192             raise error.ParseError(l.rstrip(), ("%s:%d" % (src, line)))
   208             raise error.ParseError(l.rstrip(), ("%s:%d" % (src, line)))
   193 
   209 
   194     def read(self, path, fp=None, sections=None, remap=None):
   210     def read(self, path, fp=None, sections=None, remap=None):
   195         if not fp:
   211         if not fp:
   196             fp = util.posixfile(path, 'rb')
   212             fp = util.posixfile(path, 'rb')
   197         assert getattr(fp, 'mode', r'rb') == r'rb', (
   213         assert (
   198             'config files must be opened in binary mode, got fp=%r mode=%r' % (
   214             getattr(fp, 'mode', r'rb') == r'rb'
   199                 fp, fp.mode))
   215         ), 'config files must be opened in binary mode, got fp=%r mode=%r' % (
   200         self.parse(path, fp.read(),
   216             fp,
   201                    sections=sections, remap=remap, include=self.read)
   217             fp.mode,
       
   218         )
       
   219         self.parse(
       
   220             path, fp.read(), sections=sections, remap=remap, include=self.read
       
   221         )
       
   222 
   202 
   223 
   203 def parselist(value):
   224 def parselist(value):
   204     """parse a configuration value as a list of comma/space separated strings
   225     """parse a configuration value as a list of comma/space separated strings
   205 
   226 
   206     >>> parselist(b'this,is "a small" ,test')
   227     >>> parselist(b'this,is "a small" ,test')
   207     ['this', 'is', 'a small', 'test']
   228     ['this', 'is', 'a small', 'test']
   208     """
   229     """
   209 
   230 
   210     def _parse_plain(parts, s, offset):
   231     def _parse_plain(parts, s, offset):
   211         whitespace = False
   232         whitespace = False
   212         while offset < len(s) and (s[offset:offset + 1].isspace()
   233         while offset < len(s) and (
   213                                    or s[offset:offset + 1] == ','):
   234             s[offset : offset + 1].isspace() or s[offset : offset + 1] == ','
       
   235         ):
   214             whitespace = True
   236             whitespace = True
   215             offset += 1
   237             offset += 1
   216         if offset >= len(s):
   238         if offset >= len(s):
   217             return None, parts, offset
   239             return None, parts, offset
   218         if whitespace:
   240         if whitespace:
   219             parts.append('')
   241             parts.append('')
   220         if s[offset:offset + 1] == '"' and not parts[-1]:
   242         if s[offset : offset + 1] == '"' and not parts[-1]:
   221             return _parse_quote, parts, offset + 1
   243             return _parse_quote, parts, offset + 1
   222         elif s[offset:offset + 1] == '"' and parts[-1][-1:] == '\\':
   244         elif s[offset : offset + 1] == '"' and parts[-1][-1:] == '\\':
   223             parts[-1] = parts[-1][:-1] + s[offset:offset + 1]
   245             parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
   224             return _parse_plain, parts, offset + 1
   246             return _parse_plain, parts, offset + 1
   225         parts[-1] += s[offset:offset + 1]
   247         parts[-1] += s[offset : offset + 1]
   226         return _parse_plain, parts, offset + 1
   248         return _parse_plain, parts, offset + 1
   227 
   249 
   228     def _parse_quote(parts, s, offset):
   250     def _parse_quote(parts, s, offset):
   229         if offset < len(s) and s[offset:offset + 1] == '"': # ""
   251         if offset < len(s) and s[offset : offset + 1] == '"':  # ""
   230             parts.append('')
   252             parts.append('')
   231             offset += 1
   253             offset += 1
   232             while offset < len(s) and (s[offset:offset + 1].isspace() or
   254             while offset < len(s) and (
   233                     s[offset:offset + 1] == ','):
   255                 s[offset : offset + 1].isspace()
       
   256                 or s[offset : offset + 1] == ','
       
   257             ):
   234                 offset += 1
   258                 offset += 1
   235             return _parse_plain, parts, offset
   259             return _parse_plain, parts, offset
   236 
   260 
   237         while offset < len(s) and s[offset:offset + 1] != '"':
   261         while offset < len(s) and s[offset : offset + 1] != '"':
   238             if (s[offset:offset + 1] == '\\' and offset + 1 < len(s)
   262             if (
   239                     and s[offset + 1:offset + 2] == '"'):
   263                 s[offset : offset + 1] == '\\'
       
   264                 and offset + 1 < len(s)
       
   265                 and s[offset + 1 : offset + 2] == '"'
       
   266             ):
   240                 offset += 1
   267                 offset += 1
   241                 parts[-1] += '"'
   268                 parts[-1] += '"'
   242             else:
   269             else:
   243                 parts[-1] += s[offset:offset + 1]
   270                 parts[-1] += s[offset : offset + 1]
   244             offset += 1
   271             offset += 1
   245 
   272 
   246         if offset >= len(s):
   273         if offset >= len(s):
   247             real_parts = _configlist(parts[-1])
   274             real_parts = _configlist(parts[-1])
   248             if not real_parts:
   275             if not real_parts:
   252                 parts = parts[:-1]
   279                 parts = parts[:-1]
   253                 parts.extend(real_parts)
   280                 parts.extend(real_parts)
   254             return None, parts, offset
   281             return None, parts, offset
   255 
   282 
   256         offset += 1
   283         offset += 1
   257         while offset < len(s) and s[offset:offset + 1] in [' ', ',']:
   284         while offset < len(s) and s[offset : offset + 1] in [' ', ',']:
   258             offset += 1
   285             offset += 1
   259 
   286 
   260         if offset < len(s):
   287         if offset < len(s):
   261             if offset + 1 == len(s) and s[offset:offset + 1] == '"':
   288             if offset + 1 == len(s) and s[offset : offset + 1] == '"':
   262                 parts[-1] += '"'
   289                 parts[-1] += '"'
   263                 offset += 1
   290                 offset += 1
   264             else:
   291             else:
   265                 parts.append('')
   292                 parts.append('')
   266         else:
   293         else: