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,2008,2009 Fluendo, S.L. 
  6  # Copyright (C) 2010,2011 Flumotion Services, S.A. 
  7  # All rights reserved. 
  8  # 
  9  # This file may be distributed and/or modified under the terms of 
 10  # the GNU Lesser General Public License version 2.1 as published by 
 11  # the Free Software Foundation. 
 12  # This file is distributed without any warranty; without even the implied 
 13  # warranty of merchantability or fitness for a particular purpose. 
 14  # See "LICENSE.LGPL" in the source distribution for more information. 
 15  # 
 16  # Headers in this file shall remain intact. 
 17   
 18  """configuration parsing utilities. 
 19  Base classes for parsing of flumotion configuration files 
 20  """ 
 21   
 22  import os 
 23  import locale 
 24  import sys 
 25  import warnings 
 26   
 27  from flumotion.common import log, common, registry, fxml 
 28  from flumotion.common.errors import ConfigError 
 29  from flumotion.common.fraction import fractionFromValue 
 30   
 31  __version__ = "$Rev$" 
 32   
 33   
34 -def parsePropertyValue(propName, type, value):
35 # XXX: We might end up calling float(), which breaks 36 # when using LC_NUMERIC when it is not C -- only in python 37 # 2.3 though, no prob in 2.4. See PEP 331 38 if sys.version_info < (2, 4): 39 locale.setlocale(locale.LC_NUMERIC, "C") 40 41 def tryStr(s): 42 try: 43 return str(s) 44 except UnicodeEncodeError: 45 return s
46 47 def strWithoutNewlines(s): 48 return tryStr(' '.join([x.strip() for x in s.split('\n')])) 49 50 def boolean(v): 51 if isinstance(v, bool): 52 return v 53 return common.strToBool(v) 54 55 def pythonInt(i): 56 try: 57 return int(i, 0) 58 except TypeError: 59 # sometimes the passed value might trip up automatic radix 60 # detection 61 return int(i) 62 63 try: 64 # yay! 65 return {'string': strWithoutNewlines, 66 'rawstring': tryStr, 67 'int': pythonInt, 68 'long': long, 69 'bool': boolean, 70 'float': float, 71 'fraction': fractionFromValue}[type](value) 72 except KeyError: 73 raise ConfigError("unknown type '%s' for property %s" 74 % (type, propName)) 75 except Exception, e: 76 raise ConfigError("Error parsing property '%s': '%s' does not " 77 "appear to be a valid %s.\nDebug: %s" 78 % (propName, value, type, 79 log.getExceptionMessage(e))) 80 81
82 -def parseCompoundPropertyValue(name, definition, value):
83 if isinstance(value, (list, tuple)): 84 try: 85 parsed = buildPropertyDict(value, definition.getProperties()) 86 except ConfigError, ce: 87 m = ('(inside compound-property %r) %s' % 88 (name, ce.args[0])) 89 raise ConfigError(m) 90 # elif isinstance(value, basestring): 91 # FIXME: parse the string representation of the compound property? 92 # pass 93 else: 94 raise ConfigError('simple value specified where compound property' 95 ' (name=%r) expected' % (name, )) 96 return parsed
97 98
99 -def buildPropertyDict(propertyList, propertySpecList):
100 """Build a property dict suitable for forming part of a component 101 config. 102 103 @param propertyList: List of property name-value pairs. For example, 104 [('foo', 'bar'), ('baz', 3)] defines two 105 property-value pairs. The values will be parsed 106 into the appropriate types, this it is allowed 107 to pass the string '3' for an int value. 108 @type propertyList: List of (name, value) 109 @param propertySpecList: The set of allowed and required properties 110 @type propertySpecList: List of 111 L{flumotion.common.registry.RegistryEntryProperty} 112 """ 113 ret = {} 114 prop_specs = dict([(x.name, x) for x in propertySpecList]) 115 for name, value in propertyList: 116 if not name in prop_specs: 117 raise ConfigError('unknown property %s' % (name, )) 118 definition = prop_specs[name] 119 120 if isinstance(definition, registry.RegistryEntryCompoundProperty): 121 parsed = parseCompoundPropertyValue(name, definition, value) 122 else: 123 if isinstance(value, (list, tuple)): 124 raise ConfigError('compound value specified where simple' 125 ' property (name=%r) expected' % (name, )) 126 parsed = parsePropertyValue(name, definition.type, value) 127 if definition.multiple: 128 vals = ret.get(name, []) 129 vals.append(parsed) 130 ret[name] = vals 131 else: 132 if name in ret: 133 raise ConfigError("multiple value specified but not " 134 "allowed for property %s" % (name, )) 135 ret[name] = parsed 136 137 for name, definition in prop_specs.items(): 138 if definition.isRequired() and not name in ret: 139 raise ConfigError("required but unspecified property %s" 140 % (name, )) 141 return ret
142 143
144 -def buildPlugsSet(plugsList, sockets):
145 """Build a plugs dict suitable for forming part of a component 146 config. 147 148 @param plugsList: List of plugs, as type-propertyList pairs. For 149 example, [('frag', [('foo', 'bar')])] defines a plug 150 of type 'frag', and the propertyList representing 151 that plug's properties. The properties will be 152 validated against the plug's properties as defined 153 in the registry. 154 @type plugsList: List of (type, propertyList) 155 @param sockets: The set of allowed sockets 156 @type sockets: List of str 157 """ 158 ret = {} 159 for socket in sockets: 160 ret[socket] = [] 161 for plugType, propertyList in plugsList: 162 plug = ConfigEntryPlug(plugType, propertyList) 163 if plug.socket not in ret: 164 raise ConfigError("Unsupported socket type: %s (not in list %s)" 165 % (plug.socket, ", ".join(ret))) 166 ret[plug.socket].append(plug.config) 167 return ret
168 169
170 -class ConfigEntryPlug(log.Loggable):
171 "I represent a <plug> entry in a planet config file" 172
173 - def __init__(self, plugType, propertyList):
174 try: 175 defs = registry.getRegistry().getPlug(plugType) 176 except KeyError: 177 raise ConfigError("unknown plug type: %s" % plugType) 178 179 self.type = plugType 180 self.socket = defs.getSocket() 181 self.properties = buildPropertyDict(propertyList, 182 defs.getProperties()) 183 self.config = {'type': self.type, 184 'socket': self.socket, 185 'entries': self._parseEntries(defs), 186 'properties': self.properties}
187
188 - def _parseEntries(self, entries):
189 d = {} 190 for entry in entries.getEntries(): 191 d[entry.getType()] = { 192 'module-name': entry.getModuleName(), 193 'function-name': entry.getFunction(), 194 } 195 return d
196 197
198 -class BaseConfigParser(fxml.Parser):
199 parserError = ConfigError 200
201 - def __init__(self, file):
202 """ 203 @param file: The file to parse, either as an open file object, 204 or as the name of a file to open. 205 @type file: str or file. 206 """ 207 self.add(file)
208
209 - def add(self, file):
210 """ 211 @param file: The file to parse, either as an open file object, 212 or as the name of a file to open. 213 @type file: str or file. 214 """ 215 try: 216 self.path = os.path.split(file.name)[0] 217 except AttributeError: 218 # for file objects without the name attribute, e.g. StringIO 219 self.path = None 220 221 try: 222 self.doc = self.getRoot(file) 223 except fxml.ParserError, e: 224 raise ConfigError(e.args[0])
225
226 - def getPath(self):
227 return self.path
228
229 - def parsePlugs(self, node):
230 # <plugs> 231 # <plug> 232 # returns: list of (socket, type, properties) 233 self.checkAttributes(node) 234 235 plugs = [] 236 237 def parsePlug(node): 238 # <plug type=...> 239 # <property> 240 # F0.10 241 # socket is unneeded and deprecated; we don't use it. 242 plugType, socket = self.parseAttributes( 243 node, ('type', ), ('socket', )) 244 if socket is not None: 245 msg = ('"socket" attribute of plug tag is not used' 246 ' and has been deprecated, please update your' 247 ' configuration file (found offending plug of type' 248 ' %r)' % plugType) 249 warnings.warn(msg, DeprecationWarning) 250 properties = [] 251 parsers = {'property': (self._parseProperty, properties.append), 252 'compound-property': (self._parseCompoundProperty, 253 properties.append)} 254 self.parseFromTable(node, parsers) 255 return plugType, properties
256 257 parsers = {'plug': (parsePlug, plugs.append)} 258 self.parseFromTable(node, parsers) 259 return plugs
260
261 - def _parseProperty(self, node):
262 name, = self.parseAttributes(node, ('name', )) 263 return name, self.parseTextNode(node, lambda x: x)
264
265 - def _parseCompoundProperty(self, node):
266 # <compound-property name="name"> 267 # <property name="name">value</property>* 268 # <compound-property name="name">...</compound-property>* 269 # </compound-property> 270 name, = self.parseAttributes(node, ('name', )) 271 properties = [] 272 parsers = {'property': (self._parseProperty, properties.append), 273 'compound-property': (self._parseCompoundProperty, 274 properties.append)} 275 self.parseFromTable(node, parsers) 276 return name, properties
277