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