1 import re
2 import urlparse
3 import urlgrabber
4 import os.path
5
6 import Errors
7
8
9 _KEYCRE = re.compile(r"\$(\w+)")
10
12 '''Perform variable replacement
13
14 @param raw: String to perform substitution on.
15 @param vars: Dictionary of variables to replace. Key is variable name
16 (without $ prefix). Value is replacement string.
17 @return: Input raw string with substituted values.
18 '''
19
20 done = []
21
22 while raw:
23 m = _KEYCRE.search(raw)
24 if not m:
25 done.append(raw)
26 break
27
28
29
30 varname = m.group(1).lower()
31 replacement = vars.get(varname, m.group())
32
33 start, end = m.span()
34 done.append(raw[:start])
35 done.append(replacement)
36 raw = raw[end:]
37
38 return ''.join(done)
39
41 """
42 ConfigParser Include Pre-Processor
43
44 File-like Object capable of pre-processing include= lines for
45 a ConfigParser.
46
47 The readline function expands lines matching include=(url)
48 into lines from the url specified. Includes may occur in
49 included files as well.
50
51 Suggested Usage::
52 cfg = ConfigParser.ConfigParser()
53 fileobj = confpp( fileorurl )
54 cfg.readfp(fileobj)
55 """
56
57
58 - def __init__(self, configfile, vars=None):
59
60 self._vars = vars
61
62
63 self._section = None
64
65
66
67 self.mode = 'r'
68
69
70
71 scheme = urlparse.urlparse(configfile)[0]
72 if scheme == '':
73
74 if configfile[0] != '/':
75 configfile = os.getcwd() + '/' + configfile
76 url = 'file://' + configfile
77 else:
78 url = configfile
79
80
81
82 self._incstack = []
83 self._alreadyincluded = []
84
85
86 fo = self._pushfile( url )
87 if fo is None:
88 raise Errors.ConfigError, 'Error accessing file: %s' % url
89
91 """
92 Implementation of File-Like Object readline function. This should be
93 the only function called by ConfigParser according to the python docs.
94 We maintain a stack of real FLOs and delegate readline calls to the
95 FLO on top of the stack. When EOF occurs on the topmost FLO, it is
96 popped off the stack and the next FLO takes over. include= lines
97 found anywhere cause a new FLO to be opened and pushed onto the top
98 of the stack. Finally, we return EOF when the bottom-most (configfile
99 arg to __init__) FLO returns EOF.
100
101 Very Technical Pseudo Code::
102
103 def confpp.readline() [this is called by ConfigParser]
104 open configfile, push on stack
105 while stack has some stuff on it
106 line = readline from file on top of stack
107 pop and continue if line is EOF
108 if line starts with 'include=' then
109 error if file is recursive or duplicate
110 otherwise open file, push on stack
111 continue
112 else
113 return line
114
115 return EOF
116 """
117
118
119 line=''
120 while len(self._incstack) > 0:
121
122 fo = self._incstack[-1]
123 line = fo.readline()
124 if len(line) > 0:
125 m = re.match( r'\s*include\s*=\s*(?P<url>.*)', line )
126 if m:
127 url = m.group('url')
128 if len(url) == 0:
129 raise Errors.ConfigError, \
130 'Error parsing config %s: include must specify file to include.' % (self.name)
131 else:
132
133 fo = self._pushfile( url )
134 else:
135
136 secmatch = re.match( r'\s*\[(?P<section>.*)\]', line )
137 if secmatch:
138 self._section = secmatch.group('section')
139
140
141 break
142 else:
143
144 self._popfile()
145
146
147
148 if self._vars:
149 return varReplace(line, self._vars)
150 return line
151
152
154 """
155 Returns an absolute url for the (possibly) relative
156 url specified. The base url used to resolve the
157 missing bits of url is the url of the file currently
158 being included (i.e. the top of the stack).
159 """
160
161 if len(self._incstack) == 0:
162
163 return url
164 else:
165 return urlparse.urljoin( self.geturl(), url )
166
168 """
169 Opens the url specified, pushes it on the stack, and
170 returns a file like object. Returns None if the url
171 has previously been included.
172 If the file can not be opened this function exits.
173 """
174
175
176
177 absurl = self._absurl(url)
178
179
180
181 includetuple = (absurl, self._section)
182
183 if self._isalreadyincluded(includetuple):
184 return None
185 try:
186 fo = urlgrabber.grabber.urlopen(absurl)
187 except urlgrabber.grabber.URLGrabError, e:
188 fo = None
189 if fo is not None:
190 self.name = absurl
191 self._incstack.append( fo )
192 self._alreadyincluded.append(includetuple)
193 else:
194 raise Errors.ConfigError, \
195 'Error accessing file for config %s' % (absurl)
196
197 return fo
198
199
201 """
202 Pop a file off the stack signaling completion of including that file.
203 """
204 fo = self._incstack.pop()
205 fo.close()
206 if len(self._incstack) > 0:
207 self.name = self._incstack[-1].geturl()
208 else:
209 self.name = None
210
211
213 """
214 Checks if the tuple describes an include that was already done.
215 This does not necessarily have to be recursive
216 """
217 for etuple in self._alreadyincluded:
218 if etuple == tuple: return 1
219 return 0
220
221
223