##// END OF EJS Templates
config: guard against setconfig specifying unicode values on py3...
Augie Fackler -
r31306:c920efa9 default
parent child Browse files
Show More
@@ -1,177 +1,181
1 1 # config.py - configuration parsing for Mercurial
2 2 #
3 3 # Copyright 2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import errno
11 11 import os
12 12
13 13 from .i18n import _
14 14 from . import (
15 15 error,
16 pycompat,
16 17 util,
17 18 )
18 19
19 20 class config(object):
20 21 def __init__(self, data=None, includepaths=[]):
21 22 self._data = {}
22 23 self._source = {}
23 24 self._unset = []
24 25 self._includepaths = includepaths
25 26 if data:
26 27 for k in data._data:
27 28 self._data[k] = data[k].copy()
28 29 self._source = data._source.copy()
29 30 def copy(self):
30 31 return config(self)
31 32 def __contains__(self, section):
32 33 return section in self._data
33 34 def hasitem(self, section, item):
34 35 return item in self._data.get(section, {})
35 36 def __getitem__(self, section):
36 37 return self._data.get(section, {})
37 38 def __iter__(self):
38 39 for d in self.sections():
39 40 yield d
40 41 def update(self, src):
41 42 for s, n in src._unset:
42 43 if s in self and n in self._data[s]:
43 44 del self._data[s][n]
44 45 del self._source[(s, n)]
45 46 for s in src:
46 47 if s not in self:
47 48 self._data[s] = util.sortdict()
48 49 self._data[s].update(src._data[s])
49 50 self._source.update(src._source)
50 51 def get(self, section, item, default=None):
51 52 return self._data.get(section, {}).get(item, default)
52 53
53 54 def backup(self, section, item):
54 55 """return a tuple allowing restore to reinstall a previous value
55 56
56 57 The main reason we need it is because it handles the "no data" case.
57 58 """
58 59 try:
59 60 value = self._data[section][item]
60 61 source = self.source(section, item)
61 62 return (section, item, value, source)
62 63 except KeyError:
63 64 return (section, item)
64 65
65 66 def source(self, section, item):
66 67 return self._source.get((section, item), "")
67 68 def sections(self):
68 69 return sorted(self._data.keys())
69 70 def items(self, section):
70 71 return self._data.get(section, {}).items()
71 72 def set(self, section, item, value, source=""):
73 if pycompat.ispy3:
74 assert not isinstance(value, str), (
75 'config values may not be unicode strings on Python 3')
72 76 if section not in self:
73 77 self._data[section] = util.sortdict()
74 78 self._data[section][item] = value
75 79 if source:
76 80 self._source[(section, item)] = source
77 81
78 82 def restore(self, data):
79 83 """restore data returned by self.backup"""
80 84 if len(data) == 4:
81 85 # restore old data
82 86 section, item, value, source = data
83 87 self._data[section][item] = value
84 88 self._source[(section, item)] = source
85 89 else:
86 90 # no data before, remove everything
87 91 section, item = data
88 92 if section in self._data:
89 93 self._data[section].pop(item, None)
90 94 self._source.pop((section, item), None)
91 95
92 96 def parse(self, src, data, sections=None, remap=None, include=None):
93 97 sectionre = util.re.compile(br'\[([^\[]+)\]')
94 98 itemre = util.re.compile(br'([^=\s][^=]*?)\s*=\s*(.*\S|)')
95 99 contre = util.re.compile(br'\s+(\S|\S.*\S)\s*$')
96 100 emptyre = util.re.compile(br'(;|#|\s*$)')
97 101 commentre = util.re.compile(br'(;|#)')
98 102 unsetre = util.re.compile(br'%unset\s+(\S+)')
99 103 includere = util.re.compile(br'%include\s+(\S|\S.*\S)\s*$')
100 104 section = ""
101 105 item = None
102 106 line = 0
103 107 cont = False
104 108
105 109 for l in data.splitlines(True):
106 110 line += 1
107 111 if line == 1 and l.startswith('\xef\xbb\xbf'):
108 112 # Someone set us up the BOM
109 113 l = l[3:]
110 114 if cont:
111 115 if commentre.match(l):
112 116 continue
113 117 m = contre.match(l)
114 118 if m:
115 119 if sections and section not in sections:
116 120 continue
117 121 v = self.get(section, item) + "\n" + m.group(1)
118 122 self.set(section, item, v, "%s:%d" % (src, line))
119 123 continue
120 124 item = None
121 125 cont = False
122 126 m = includere.match(l)
123 127
124 128 if m and include:
125 129 expanded = util.expandpath(m.group(1))
126 130 includepaths = [os.path.dirname(src)] + self._includepaths
127 131
128 132 for base in includepaths:
129 133 inc = os.path.normpath(os.path.join(base, expanded))
130 134
131 135 try:
132 136 include(inc, remap=remap, sections=sections)
133 137 break
134 138 except IOError as inst:
135 139 if inst.errno != errno.ENOENT:
136 140 raise error.ParseError(_("cannot include %s (%s)")
137 141 % (inc, inst.strerror),
138 142 "%s:%s" % (src, line))
139 143 continue
140 144 if emptyre.match(l):
141 145 continue
142 146 m = sectionre.match(l)
143 147 if m:
144 148 section = m.group(1)
145 149 if remap:
146 150 section = remap.get(section, section)
147 151 if section not in self:
148 152 self._data[section] = util.sortdict()
149 153 continue
150 154 m = itemre.match(l)
151 155 if m:
152 156 item = m.group(1)
153 157 cont = True
154 158 if sections and section not in sections:
155 159 continue
156 160 self.set(section, item, m.group(2), "%s:%d" % (src, line))
157 161 continue
158 162 m = unsetre.match(l)
159 163 if m:
160 164 name = m.group(1)
161 165 if sections and section not in sections:
162 166 continue
163 167 if self.get(section, name) is not None:
164 168 del self._data[section][name]
165 169 self._unset.append((section, name))
166 170 continue
167 171
168 172 raise error.ParseError(l.rstrip(), ("%s:%s" % (src, line)))
169 173
170 174 def read(self, path, fp=None, sections=None, remap=None):
171 175 if not fp:
172 176 fp = util.posixfile(path, 'rb')
173 177 assert getattr(fp, 'mode', r'rb') == r'rb', (
174 178 'config files must be opened in binary mode, got fp=%r mode=%r' % (
175 179 fp, fp.mode))
176 180 self.parse(path, fp.read(),
177 181 sections=sections, remap=remap, include=self.read)
General Comments 0
You need to be logged in to leave comments. Login now