mercurial/config.py
changeset 43077 687b865b95ad
parent 43076 2372284d9457
child 43089 c59eb1560c44
equal deleted inserted replaced
43076:2372284d9457 43077:687b865b95ad
    77             return (section, item, value, source)
    77             return (section, item, value, source)
    78         except KeyError:
    78         except KeyError:
    79             return (section, item)
    79             return (section, item)
    80 
    80 
    81     def source(self, section, item):
    81     def source(self, section, item):
    82         return self._source.get((section, item), "")
    82         return self._source.get((section, item), b"")
    83 
    83 
    84     def sections(self):
    84     def sections(self):
    85         return sorted(self._data.keys())
    85         return sorted(self._data.keys())
    86 
    86 
    87     def items(self, section):
    87     def items(self, section):
    88         return list(self._data.get(section, {}).iteritems())
    88         return list(self._data.get(section, {}).iteritems())
    89 
    89 
    90     def set(self, section, item, value, source=""):
    90     def set(self, section, item, value, source=b""):
    91         if pycompat.ispy3:
    91         if pycompat.ispy3:
    92             assert not isinstance(
    92             assert not isinstance(
    93                 section, str
    93                 section, str
    94             ), 'config section may not be unicode strings on Python 3'
    94             ), b'config section may not be unicode strings on Python 3'
    95             assert not isinstance(
    95             assert not isinstance(
    96                 item, str
    96                 item, str
    97             ), 'config item may not be unicode strings on Python 3'
    97             ), b'config item may not be unicode strings on Python 3'
    98             assert not isinstance(
    98             assert not isinstance(
    99                 value, str
    99                 value, str
   100             ), 'config values may not be unicode strings on Python 3'
   100             ), b'config values may not be unicode strings on Python 3'
   101         if section not in self:
   101         if section not in self:
   102             self._data[section] = util.cowsortdict()
   102             self._data[section] = util.cowsortdict()
   103         else:
   103         else:
   104             self._data[section] = self._data[section].preparewrite()
   104             self._data[section] = self._data[section].preparewrite()
   105         self._data[section][item] = value
   105         self._data[section][item] = value
   129         contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
   129         contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
   130         emptyre = util.re.compile(br'(;|#|\s*$)')
   130         emptyre = util.re.compile(br'(;|#|\s*$)')
   131         commentre = util.re.compile(br'(;|#)')
   131         commentre = util.re.compile(br'(;|#)')
   132         unsetre = util.re.compile(br'%unset\s+(\S+)')
   132         unsetre = util.re.compile(br'%unset\s+(\S+)')
   133         includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
   133         includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
   134         section = ""
   134         section = b""
   135         item = None
   135         item = None
   136         line = 0
   136         line = 0
   137         cont = False
   137         cont = False
   138 
   138 
   139         if remap:
   139         if remap:
   140             section = remap.get(section, section)
   140             section = remap.get(section, section)
   141 
   141 
   142         for l in data.splitlines(True):
   142         for l in data.splitlines(True):
   143             line += 1
   143             line += 1
   144             if line == 1 and l.startswith('\xef\xbb\xbf'):
   144             if line == 1 and l.startswith(b'\xef\xbb\xbf'):
   145                 # Someone set us up the BOM
   145                 # Someone set us up the BOM
   146                 l = l[3:]
   146                 l = l[3:]
   147             if cont:
   147             if cont:
   148                 if commentre.match(l):
   148                 if commentre.match(l):
   149                     continue
   149                     continue
   150                 m = contre.match(l)
   150                 m = contre.match(l)
   151                 if m:
   151                 if m:
   152                     if sections and section not in sections:
   152                     if sections and section not in sections:
   153                         continue
   153                         continue
   154                     v = self.get(section, item) + "\n" + m.group(1)
   154                     v = self.get(section, item) + b"\n" + m.group(1)
   155                     self.set(section, item, v, "%s:%d" % (src, line))
   155                     self.set(section, item, v, b"%s:%d" % (src, line))
   156                     continue
   156                     continue
   157                 item = None
   157                 item = None
   158                 cont = False
   158                 cont = False
   159             m = includere.match(l)
   159             m = includere.match(l)
   160 
   160 
   169                         include(inc, remap=remap, sections=sections)
   169                         include(inc, remap=remap, sections=sections)
   170                         break
   170                         break
   171                     except IOError as inst:
   171                     except IOError as inst:
   172                         if inst.errno != errno.ENOENT:
   172                         if inst.errno != errno.ENOENT:
   173                             raise error.ParseError(
   173                             raise error.ParseError(
   174                                 _("cannot include %s (%s)")
   174                                 _(b"cannot include %s (%s)")
   175                                 % (inc, inst.strerror),
   175                                 % (inc, inst.strerror),
   176                                 "%s:%d" % (src, line),
   176                                 b"%s:%d" % (src, line),
   177                             )
   177                             )
   178                 continue
   178                 continue
   179             if emptyre.match(l):
   179             if emptyre.match(l):
   180                 continue
   180                 continue
   181             m = sectionre.match(l)
   181             m = sectionre.match(l)
   190             if m:
   190             if m:
   191                 item = m.group(1)
   191                 item = m.group(1)
   192                 cont = True
   192                 cont = True
   193                 if sections and section not in sections:
   193                 if sections and section not in sections:
   194                     continue
   194                     continue
   195                 self.set(section, item, m.group(2), "%s:%d" % (src, line))
   195                 self.set(section, item, m.group(2), b"%s:%d" % (src, line))
   196                 continue
   196                 continue
   197             m = unsetre.match(l)
   197             m = unsetre.match(l)
   198             if m:
   198             if m:
   199                 name = m.group(1)
   199                 name = m.group(1)
   200                 if sections and section not in sections:
   200                 if sections and section not in sections:
   203                     self._data[section] = self._data[section].preparewrite()
   203                     self._data[section] = self._data[section].preparewrite()
   204                     del self._data[section][name]
   204                     del self._data[section][name]
   205                 self._unset.append((section, name))
   205                 self._unset.append((section, name))
   206                 continue
   206                 continue
   207 
   207 
   208             raise error.ParseError(l.rstrip(), ("%s:%d" % (src, line)))
   208             raise error.ParseError(l.rstrip(), (b"%s:%d" % (src, line)))
   209 
   209 
   210     def read(self, path, fp=None, sections=None, remap=None):
   210     def read(self, path, fp=None, sections=None, remap=None):
   211         if not fp:
   211         if not fp:
   212             fp = util.posixfile(path, 'rb')
   212             fp = util.posixfile(path, b'rb')
   213         assert (
   213         assert (
   214             getattr(fp, 'mode', r'rb') == r'rb'
   214             getattr(fp, 'mode', r'rb') == r'rb'
   215         ), 'config files must be opened in binary mode, got fp=%r mode=%r' % (
   215         ), b'config files must be opened in binary mode, got fp=%r mode=%r' % (
   216             fp,
   216             fp,
   217             fp.mode,
   217             fp.mode,
   218         )
   218         )
   219         self.parse(
   219         self.parse(
   220             path, fp.read(), sections=sections, remap=remap, include=self.read
   220             path, fp.read(), sections=sections, remap=remap, include=self.read
   229     """
   229     """
   230 
   230 
   231     def _parse_plain(parts, s, offset):
   231     def _parse_plain(parts, s, offset):
   232         whitespace = False
   232         whitespace = False
   233         while offset < len(s) and (
   233         while offset < len(s) and (
   234             s[offset : offset + 1].isspace() or s[offset : offset + 1] == ','
   234             s[offset : offset + 1].isspace() or s[offset : offset + 1] == b','
   235         ):
   235         ):
   236             whitespace = True
   236             whitespace = True
   237             offset += 1
   237             offset += 1
   238         if offset >= len(s):
   238         if offset >= len(s):
   239             return None, parts, offset
   239             return None, parts, offset
   240         if whitespace:
   240         if whitespace:
   241             parts.append('')
   241             parts.append(b'')
   242         if s[offset : offset + 1] == '"' and not parts[-1]:
   242         if s[offset : offset + 1] == b'"' and not parts[-1]:
   243             return _parse_quote, parts, offset + 1
   243             return _parse_quote, parts, offset + 1
   244         elif s[offset : offset + 1] == '"' and parts[-1][-1:] == '\\':
   244         elif s[offset : offset + 1] == b'"' and parts[-1][-1:] == b'\\':
   245             parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
   245             parts[-1] = parts[-1][:-1] + s[offset : offset + 1]
   246             return _parse_plain, parts, offset + 1
   246             return _parse_plain, parts, offset + 1
   247         parts[-1] += s[offset : offset + 1]
   247         parts[-1] += s[offset : offset + 1]
   248         return _parse_plain, parts, offset + 1
   248         return _parse_plain, parts, offset + 1
   249 
   249 
   250     def _parse_quote(parts, s, offset):
   250     def _parse_quote(parts, s, offset):
   251         if offset < len(s) and s[offset : offset + 1] == '"':  # ""
   251         if offset < len(s) and s[offset : offset + 1] == b'"':  # ""
   252             parts.append('')
   252             parts.append(b'')
   253             offset += 1
   253             offset += 1
   254             while offset < len(s) and (
   254             while offset < len(s) and (
   255                 s[offset : offset + 1].isspace()
   255                 s[offset : offset + 1].isspace()
   256                 or s[offset : offset + 1] == ','
   256                 or s[offset : offset + 1] == b','
   257             ):
   257             ):
   258                 offset += 1
   258                 offset += 1
   259             return _parse_plain, parts, offset
   259             return _parse_plain, parts, offset
   260 
   260 
   261         while offset < len(s) and s[offset : offset + 1] != '"':
   261         while offset < len(s) and s[offset : offset + 1] != b'"':
   262             if (
   262             if (
   263                 s[offset : offset + 1] == '\\'
   263                 s[offset : offset + 1] == b'\\'
   264                 and offset + 1 < len(s)
   264                 and offset + 1 < len(s)
   265                 and s[offset + 1 : offset + 2] == '"'
   265                 and s[offset + 1 : offset + 2] == b'"'
   266             ):
   266             ):
   267                 offset += 1
   267                 offset += 1
   268                 parts[-1] += '"'
   268                 parts[-1] += b'"'
   269             else:
   269             else:
   270                 parts[-1] += s[offset : offset + 1]
   270                 parts[-1] += s[offset : offset + 1]
   271             offset += 1
   271             offset += 1
   272 
   272 
   273         if offset >= len(s):
   273         if offset >= len(s):
   274             real_parts = _configlist(parts[-1])
   274             real_parts = _configlist(parts[-1])
   275             if not real_parts:
   275             if not real_parts:
   276                 parts[-1] = '"'
   276                 parts[-1] = b'"'
   277             else:
   277             else:
   278                 real_parts[0] = '"' + real_parts[0]
   278                 real_parts[0] = b'"' + real_parts[0]
   279                 parts = parts[:-1]
   279                 parts = parts[:-1]
   280                 parts.extend(real_parts)
   280                 parts.extend(real_parts)
   281             return None, parts, offset
   281             return None, parts, offset
   282 
   282 
   283         offset += 1
   283         offset += 1
   284         while offset < len(s) and s[offset : offset + 1] in [' ', ',']:
   284         while offset < len(s) and s[offset : offset + 1] in [b' ', b',']:
   285             offset += 1
   285             offset += 1
   286 
   286 
   287         if offset < len(s):
   287         if offset < len(s):
   288             if offset + 1 == len(s) and s[offset : offset + 1] == '"':
   288             if offset + 1 == len(s) and s[offset : offset + 1] == b'"':
   289                 parts[-1] += '"'
   289                 parts[-1] += b'"'
   290                 offset += 1
   290                 offset += 1
   291             else:
   291             else:
   292                 parts.append('')
   292                 parts.append(b'')
   293         else:
   293         else:
   294             return None, parts, offset
   294             return None, parts, offset
   295 
   295 
   296         return _parse_plain, parts, offset
   296         return _parse_plain, parts, offset
   297 
   297 
   298     def _configlist(s):
   298     def _configlist(s):
   299         s = s.rstrip(' ,')
   299         s = s.rstrip(b' ,')
   300         if not s:
   300         if not s:
   301             return []
   301             return []
   302         parser, parts, offset = _parse_plain, [''], 0
   302         parser, parts, offset = _parse_plain, [b''], 0
   303         while parser:
   303         while parser:
   304             parser, parts, offset = parser(parts, s, offset)
   304             parser, parts, offset = parser(parts, s, offset)
   305         return parts
   305         return parts
   306 
   306 
   307     if value is not None and isinstance(value, bytes):
   307     if value is not None and isinstance(value, bytes):
   308         result = _configlist(value.lstrip(' ,\n'))
   308         result = _configlist(value.lstrip(b' ,\n'))
   309     else:
   309     else:
   310         result = value
   310         result = value
   311     return result or []
   311     return result or []