1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """configuration parsing utilities.
23 Base classes for parsing of flumotion configuration files
24 """
25
26 import os
27 import locale
28 import sys
29 import warnings
30
31 from flumotion.common import log, common, registry, fxml
32 from flumotion.common.errors import ConfigError
33 from flumotion.common.fraction import fractionFromValue
34
35 __version__ = "$Rev$"
36
37
39
40
41
42 if sys.version_info < (2, 4):
43 locale.setlocale(locale.LC_NUMERIC, "C")
44
45 def tryStr(s):
46 try:
47 return str(s)
48 except UnicodeEncodeError:
49 return s
50
51 def strWithoutNewlines(s):
52 return tryStr(' '.join([x.strip() for x in s.split('\n')]))
53
54 def boolean(v):
55 if isinstance(v, bool):
56 return v
57 return common.strToBool(v)
58
59 def pythonInt(i):
60 try:
61 return int(i, 0)
62 except TypeError:
63
64
65 return int(i)
66
67 try:
68
69 return {'string': strWithoutNewlines,
70 'rawstring': tryStr,
71 'int': pythonInt,
72 'long': long,
73 'bool': boolean,
74 'float': float,
75 'fraction': fractionFromValue}[type](value)
76 except KeyError:
77 raise ConfigError("unknown type '%s' for property %s"
78 % (type, propName))
79 except Exception, e:
80 raise ConfigError("Error parsing property '%s': '%s' does not "
81 "appear to be a valid %s.\nDebug: %s"
82 % (propName, value, type,
83 log.getExceptionMessage(e)))
84
85
101
102
104 """Build a property dict suitable for forming part of a component
105 config.
106
107 @param propertyList: List of property name-value pairs. For example,
108 [('foo', 'bar'), ('baz', 3)] defines two
109 property-value pairs. The values will be parsed
110 into the appropriate types, this it is allowed
111 to pass the string '3' for an int value.
112 @type propertyList: List of (name, value)
113 @param propertySpecList: The set of allowed and required properties
114 @type propertySpecList: List of
115 L{flumotion.common.registry.RegistryEntryProperty}
116 """
117 ret = {}
118 prop_specs = dict([(x.name, x) for x in propertySpecList])
119 for name, value in propertyList:
120 if not name in prop_specs:
121 raise ConfigError('unknown property %s' % (name, ))
122 definition = prop_specs[name]
123
124 if isinstance(definition, registry.RegistryEntryCompoundProperty):
125 parsed = parseCompoundPropertyValue(name, definition, value)
126 else:
127 if isinstance(value, (list, tuple)):
128 raise ConfigError('compound value specified where simple'
129 ' property (name=%r) expected' % (name, ))
130 parsed = parsePropertyValue(name, definition.type, value)
131 if definition.multiple:
132 vals = ret.get(name, [])
133 vals.append(parsed)
134 ret[name] = vals
135 else:
136 if name in ret:
137 raise ConfigError("multiple value specified but not "
138 "allowed for property %s" % (name, ))
139 ret[name] = parsed
140
141 for name, definition in prop_specs.items():
142 if definition.isRequired() and not name in ret:
143 raise ConfigError("required but unspecified property %s"
144 % (name, ))
145 return ret
146
147
149 """Build a plugs dict suitable for forming part of a component
150 config.
151
152 @param plugsList: List of plugs, as type-propertyList pairs. For
153 example, [('frag', [('foo', 'bar')])] defines a plug
154 of type 'frag', and the propertyList representing
155 that plug's properties. The properties will be
156 validated against the plug's properties as defined
157 in the registry.
158 @type plugsList: List of (type, propertyList)
159 @param sockets: The set of allowed sockets
160 @type sockets: List of str
161 """
162 ret = {}
163 for socket in sockets:
164 ret[socket] = []
165 for plugType, propertyList in plugsList:
166 plug = ConfigEntryPlug(plugType, propertyList)
167 if plug.socket not in ret:
168 raise ConfigError("Unsupported socket type: %s (not in list %s)"
169 % (plug.socket, ", ".join(ret)))
170 ret[plug.socket].append(plug.config)
171 return ret
172
173
174 -class ConfigEntryPlug(log.Loggable):
175 "I represent a <plug> entry in a planet config file"
176
177 - def __init__(self, plugType, propertyList):
178 try:
179 defs = registry.getRegistry().getPlug(plugType)
180 except KeyError:
181 raise ConfigError("unknown plug type: %s" % plugType)
182
183 self.type = plugType
184 self.socket = defs.getSocket()
185 self.properties = buildPropertyDict(propertyList,
186 defs.getProperties())
187 self.config = {'type': self.type,
188 'socket': self.socket,
189 'entries': self._parseEntries(defs),
190 'properties': self.properties}
191
192 - def _parseEntries(self, entries):
193 d = {}
194 for entry in entries.getEntries():
195 d[entry.getType()] = {
196 'module-name': entry.getModuleName(),
197 'function-name': entry.getFunction(),
198 }
199 return d
200
201
203 parserError = ConfigError
204
206 """
207 @param file: The file to parse, either as an open file object,
208 or as the name of a file to open.
209 @type file: str or file.
210 """
211 self.add(file)
212
213 - def add(self, file):
214 """
215 @param file: The file to parse, either as an open file object,
216 or as the name of a file to open.
217 @type file: str or file.
218 """
219 try:
220 self.path = os.path.split(file.name)[0]
221 except AttributeError:
222
223 self.path = None
224
225 try:
226 self.doc = self.getRoot(file)
227 except fxml.ParserError, e:
228 raise ConfigError(e.args[0])
229
232
234
235
236
237 self.checkAttributes(node)
238
239 plugs = []
240
241 def parsePlug(node):
242
243
244
245
246 plugType, socket = self.parseAttributes(
247 node, ('type', ), ('socket', ))
248 if socket is not None:
249 msg = ('"socket" attribute of plug tag is not used'
250 ' and has been deprecated, please update your'
251 ' configuration file (found offending plug of type'
252 ' %r)' % plugType)
253 warnings.warn(msg, DeprecationWarning)
254 properties = []
255 parsers = {'property': (self._parseProperty, properties.append),
256 'compound-property': (self._parseCompoundProperty,
257 properties.append)}
258 self.parseFromTable(node, parsers)
259 return plugType, properties
260
261 parsers = {'plug': (parsePlug, plugs.append)}
262 self.parseFromTable(node, parsers)
263 return plugs
264
268
281