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