Package flumotion :: Package common :: Module config
[hide private]

Source Code for Module flumotion.common.config

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_config -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 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   
38 -def parsePropertyValue(propName, type, value):
39 # XXX: We might end up calling float(), which breaks 40 # when using LC_NUMERIC when it is not C -- only in python 41 # 2.3 though, no prob in 2.4. See PEP 331 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 # sometimes the passed value might trip up automatic radix 64 # detection 65 return int(i) 66 67 try: 68 # yay! 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
86 -def parseCompoundPropertyValue(name, definition, value):
87 if isinstance(value, (list, tuple)): 88 try: 89 parsed = buildPropertyDict(value, definition.getProperties()) 90 except ConfigError, ce: 91 m = ('(inside compound-property %r) %s' % 92 (name, ce.args[0])) 93 raise ConfigError(m) 94 # elif isinstance(value, basestring): 95 # FIXME: parse the string representation of the compound property? 96 # pass 97 else: 98 raise ConfigError('simple value specified where compound property' 99 ' (name=%r) expected' % (name, )) 100 return parsed
101 102
103 -def buildPropertyDict(propertyList, propertySpecList):
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
148 -def buildPlugsSet(plugsList, sockets):
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
202 -class BaseConfigParser(fxml.Parser):
203 parserError = ConfigError 204
205 - def __init__(self, file):
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 # for file objects without the name attribute, e.g. StringIO 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
230 - def getPath(self):
231 return self.path
232
233 - def parsePlugs(self, node):
234 # <plugs> 235 # <plug> 236 # returns: list of (socket, type, properties) 237 self.checkAttributes(node) 238 239 plugs = [] 240 241 def parsePlug(node): 242 # <plug type=...> 243 # <property> 244 # F0.10 245 # socket is unneeded and deprecated; we don't use it. 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
265 - def _parseProperty(self, node):
266 name, = self.parseAttributes(node, ('name', )) 267 return name, self.parseTextNode(node, lambda x: x)
268
269 - def _parseCompoundProperty(self, node):
270 # <compound-property name="name"> 271 # <property name="name">value</property>* 272 # <compound-property name="name">...</compound-property>* 273 # </compound-property> 274 name, = self.parseAttributes(node, ('name', )) 275 properties = [] 276 parsers = {'property': (self._parseProperty, properties.append), 277 'compound-property': (self._parseCompoundProperty, 278 properties.append)} 279 self.parseFromTable(node, parsers) 280 return name, properties
281