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