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: 7850 $" 
 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 try: 60 # yay! 61 return {'string': strWithoutNewlines, 62 'rawstring': tryStr, 63 'int': int, 64 'long': long, 65 'bool': boolean, 66 'float': float, 67 'fraction': fractionFromValue}[type](value) 68 except KeyError: 69 raise ConfigError("unknown type '%s' for property %s" 70 % (type, propName)) 71 except Exception, e: 72 raise ConfigError("Error parsing property '%s': '%s' does not " 73 "appear to be a valid %s.\nDebug: %s" 74 % (propName, value, type, 75 log.getExceptionMessage(e))) 76 77
78 -def parseCompoundPropertyValue(name, definition, value):
79 if isinstance(value, (list, tuple)): 80 try: 81 parsed = buildPropertyDict(value, definition.getProperties()) 82 except ConfigError, ce: 83 m = ('(inside compound-property %r) %s' % 84 (name, ce.args[0])) 85 raise ConfigError(m) 86 # elif isinstance(value, basestring): 87 # FIXME: parse the string representation of the compound property? 88 # pass 89 else: 90 raise ConfigError('simple value specified where compound property' 91 ' (name=%r) expected' % (name, )) 92 return parsed
93 94
95 -def buildPropertyDict(propertyList, propertySpecList):
96 """Build a property dict suitable for forming part of a component 97 config. 98 99 @param propertyList: List of property name-value pairs. For example, 100 [('foo', 'bar'), ('baz', 3)] defines two 101 property-value pairs. The values will be parsed 102 into the appropriate types, this it is allowed 103 to pass the string '3' for an int value. 104 @type propertyList: List of (name, value) 105 @param propertySpecList: The set of allowed and required properties 106 @type propertySpecList: List of 107 L{flumotion.common.registry.RegistryEntryProperty} 108 """ 109 ret = {} 110 prop_specs = dict([(x.name, x) for x in propertySpecList]) 111 for name, value in propertyList: 112 if not name in prop_specs: 113 raise ConfigError('unknown property %s' % (name, )) 114 definition = prop_specs[name] 115 116 if isinstance(definition, registry.RegistryEntryCompoundProperty): 117 parsed = parseCompoundPropertyValue(name, definition, value) 118 else: 119 if isinstance(value, (list, tuple)): 120 raise ConfigError('compound value specified where simple' 121 ' property (name=%r) expected' % (name, )) 122 parsed = parsePropertyValue(name, definition.type, value) 123 if definition.multiple: 124 vals = ret.get(name, []) 125 vals.append(parsed) 126 ret[name] = vals 127 else: 128 if name in ret: 129 raise ConfigError("multiple value specified but not " 130 "allowed for property %s" % (name, )) 131 ret[name] = parsed 132 133 for name, definition in prop_specs.items(): 134 if definition.isRequired() and not name in ret: 135 raise ConfigError("required but unspecified property %s" 136 % (name, )) 137 return ret
138 139
140 -def buildPlugsSet(plugsList, sockets):
141 """Build a plugs dict suitable for forming part of a component 142 config. 143 144 @param plugsList: List of plugs, as type-propertyList pairs. For 145 example, [('frag', [('foo', 'bar')])] defines a plug 146 of type 'frag', and the propertyList representing 147 that plug's properties. The properties will be 148 validated against the plug's properties as defined 149 in the registry. 150 @type plugsList: List of (type, propertyList) 151 @param sockets: The set of allowed sockets 152 @type sockets: List of str 153 """ 154 ret = {} 155 for socket in sockets: 156 ret[socket] = [] 157 for plugType, propertyList in plugsList: 158 plug = ConfigEntryPlug(plugType, propertyList) 159 if plug.socket not in ret: 160 raise ConfigError("Unsupported socket type: %s (not in list %s)" 161 % (plug.socket, ", ".join(ret))) 162 ret[plug.socket].append(plug.config) 163 return ret
164 165
166 -class ConfigEntryPlug(log.Loggable):
167 "I represent a <plug> entry in a planet config file" 168
169 - def __init__(self, plugType, propertyList):
170 try: 171 defs = registry.getRegistry().getPlug(plugType) 172 except KeyError: 173 raise ConfigError("unknown plug type: %s" % plugType) 174 175 self.type = plugType 176 self.socket = defs.getSocket() 177 self.properties = buildPropertyDict(propertyList, 178 defs.getProperties()) 179 self.config = {'type': self.type, 180 'socket': self.socket, 181 'entries': self._parseEntries(defs), 182 'properties': self.properties}
183
184 - def _parseEntries(self, entries):
185 d = {} 186 for entry in entries.getEntries(): 187 d[entry.getType()] = { 188 'module-name': entry.getModuleName(), 189 'function-name': entry.getFunction(), 190 } 191 return d
192 193
194 -class BaseConfigParser(fxml.Parser):
195 parserError = ConfigError 196
197 - def __init__(self, file):
198 """ 199 @param file: The file to parse, either as an open file object, 200 or as the name of a file to open. 201 @type file: str or file. 202 """ 203 self.add(file)
204
205 - def add(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 try: 212 self.path = os.path.split(file.name)[0] 213 except AttributeError: 214 # for file objects without the name attribute, e.g. StringIO 215 self.path = None 216 217 try: 218 self.doc = self.getRoot(file) 219 except fxml.ParserError, e: 220 raise ConfigError(e.args[0])
221
222 - def getPath(self):
223 return self.path
224
225 - def parsePlugs(self, node):
226 # <plugs> 227 # <plug> 228 # returns: list of (socket, type, properties) 229 self.checkAttributes(node) 230 231 plugs = [] 232 233 def parsePlug(node): 234 # <plug type=...> 235 # <property> 236 # F0.8 237 # socket is unneeded and deprecated; we don't use it. 238 plugType, socket = self.parseAttributes( 239 node, ('type', ), ('socket', )) 240 if socket is not None: 241 msg = ('"socket" attribute of plug tag is not used' 242 ' and has been deprecated, please update your' 243 ' configuration file (found offending plug of type' 244 ' %r)' % plugType) 245 warnings.warn(msg, DeprecationWarning) 246 properties = [] 247 parsers = {'property': (self._parseProperty, properties.append), 248 'compound-property': (self._parseCompoundProperty, 249 properties.append)} 250 self.parseFromTable(node, parsers) 251 return plugType, properties
252 253 parsers = {'plug': (parsePlug, plugs.append)} 254 self.parseFromTable(node, parsers) 255 return plugs
256
257 - def _parseProperty(self, node):
258 name, = self.parseAttributes(node, ('name', )) 259 return name, self.parseTextNode(node, lambda x: x)
260
261 - def _parseCompoundProperty(self, node):
262 # <compound-property name="name"> 263 # <property name="name">value</property>* 264 # <compound-property name="name">...</compound-property>* 265 # </compound-property> 266 name, = self.parseAttributes(node, ('name', )) 267 properties = [] 268 parsers = {'property': (self._parseProperty, properties.append), 269 'compound-property': (self._parseCompoundProperty, 270 properties.append)} 271 self.parseFromTable(node, parsers) 272 return name, properties
273