Package flumotion :: Package launch :: Module parse
[hide private]

Source Code for Module flumotion.launch.parse

  1  # -*- Mode: Python -*- 
  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   
 23  """ 
 24  flumotion.launch.parse: A parsing library for flumotion-launch syntax. 
 25  """ 
 26   
 27   
 28  import copy 
 29  import sys 
 30   
 31  from flumotion.common import log, common, dag, registry 
 32   
 33  __all__ = ['parse_args'] 
 34   
 35   
36 -def err(x):
37 sys.stderr.write(x + '\n') 38 raise SystemExit(1)
39 40
41 -class Component(object):
42 __slots__ = ['type', 'name', 'properties', 'plugs', 'feed', 43 'source', 'clock_master', '_reg'] 44
45 - def __init__(self, type, name):
46 self.type = type 47 self.name = name 48 self.properties = {} 49 self.plugs = [] 50 self.feed = [] 51 self.source = [] 52 self.clock_master = None 53 54 r = registry.getRegistry() 55 if not r.hasComponent(self.type): 56 err('Unknown component type: %s' % self.type) 57 58 self._reg = r.getComponent(self.type)
59
60 - def verify_source(self):
61 eaters = self._reg.getEaters() 62 if eaters: 63 required = [x for x in eaters if x.getRequired()] 64 multiple = [x for x in eaters if x.getMultiple()] 65 if required and not self.source: 66 err('Component %s wants to eat but you didn\'t give it ' 67 'food' % self.name) 68 if not multiple and len(self.source) > 1: 69 err('Component %s can only eat from one feeder' % self.name) 70 else: 71 if self.source: 72 err('Component %s can\'t eat from anything' % self.name)
73
74 - def _complete_props(self, dname, props, specs):
75 def parse_fraction(v): 76 split = v.split('/') 77 assert len(split) == 2, \ 78 "Fraction values should be in the form N/D" 79 return (int(split[0]), int(split[1]))
80 81 ret = {} 82 compprops = dict([(x.getName(), x) for x in specs]) 83 84 for k, v in props.items(): 85 if k not in compprops: 86 err('Component %s has no such property `%s\'' % (dname, k)) 87 88 t = compprops[k].getType() 89 parsers = {'int': int, 90 'long': long, 91 'float': float, 92 'bool': common.strToBool, 93 'string': str, 94 'fraction': parse_fraction} 95 96 try: 97 parser = parsers[t] 98 except KeyError: 99 err('Unknown type `%s\' of property %s in component %s' 100 % (t, k, dname)) 101 102 val = parser(v) 103 104 if compprops[k].isMultiple(): 105 if not k in ret: 106 ret[k] = [] 107 ret[k].append(val) 108 else: 109 ret[k] = val 110 111 for k, v in compprops.items(): 112 if v.isRequired() and not k in ret: 113 err('Component %s missing required property `%s\' of type %s' 114 % (dname, k, v.getType())) 115 116 return ret
117
118 - def complete_and_verify_properties(self):
119 return self._complete_props(self.name, self.properties, 120 self._reg.getProperties())
121
122 - def complete_and_verify_plugs(self):
123 r = registry.getRegistry() 124 ret = {} 125 for socket in self._reg.getSockets(): 126 ret[socket] = [] 127 for plugtype, plugprops in self.plugs: 128 if not r.hasPlug(plugtype): 129 err('Unknown plug type: %s' % plugtype) 130 spec = r.getPlug(plugtype) 131 socket = spec.getSocket() 132 if not socket in ret: 133 err('Cannot add plug %s to component %s: ' 134 'sockets of type %s not supported' 135 % (plugtype, self.name, socket)) 136 props = self._complete_props(plugtype, plugprops, 137 spec.getProperties()) 138 plug = {'type': plugtype, 'socket': socket, 139 'properties': props} 140 ret[socket].append(plug) 141 return ret
142
143 - def complete_and_verify(self):
144 c = self._reg 145 146 self.feed = c.getFeeders() 147 148 self.properties = self.complete_and_verify_properties() 149 150 self.verify_source() 151 152 # fixme: 'feed' is not strictly necessary in config 153 self.feed = c.getFeeders() 154 155 # not used by the component -- see notes in _parseComponent in 156 # config.py 157 if c.getNeedsSynchronization(): 158 self.clock_master = c.getClockPriority() 159 160 self.plugs = self.complete_and_verify_plugs()
161
162 - def as_config_dict(self):
163 ret = {'name': self.name, 164 'type': self.type, 165 'properties': copy.deepcopy(self.properties), 166 'plugs': copy.deepcopy(self.properties), 167 'feed': copy.deepcopy(self.feed), 168 'clock-master': copy.deepcopy(self.clock_master), 169 'plugs': copy.deepcopy(self.plugs)} 170 if self.source: 171 ret['source'] = copy.deepcopy(self.source) 172 return ret
173
174 -class ComponentStore:
175 - def __init__(self):
176 self._names = {} 177 self._last_component = None 178 self.components = {} 179 assert not self # make sure that i am false if empty
180
181 - def _make_name(self, type):
182 i = self._names.get(type, 0) 183 self._names[type] = i + 1 184 return '%s%d' % (type, i)
185
186 - def add(self, type):
187 self._last_component = name = self._make_name(type) 188 self.components[name] = Component(type, name)
189
190 - def add_plug_to_current(self, type, props):
191 self[self.last()].plugs.append((type, props))
192
193 - def add_prop_to_current(self, key, val):
194 self[self.last()].properties[key] = val
195
196 - def last(self):
197 assert self._last_component 198 return self._last_component
199
200 - def names(self):
201 return self.components.keys()
202
204 for name in self.components: 205 self.components[name].complete_and_verify()
206
207 - def sorted_configs(self, partial_orders):
208 sort = dag.topological_sort 209 return [self[name].as_config_dict() 210 for name in sort(self.names(), partial_orders)]
211
212 - def __getitem__(self, key):
213 return self.components[key]
214
215 - def __setitem__(self, key, val):
216 self.components[key] = val
217
218 - def __contains__(self, key):
219 return key in self.components
220
221 - def __len__(self):
222 return len(self.components)
223
224 - def __iter__(self):
225 return self.components.__iter__()
226
227 -class Linker:
228 - def __init__(self, get_last_component):
229 # links: [(feedercomponentname, feeder, eatercomponentname, eater), ...] 230 self.links = [] 231 self._tmp = None 232 self.get_last_component = get_last_component
233
234 - def pending(self):
235 return bool(self._tmp)
236 265 271 312
313 - def get_sort_order(self):
314 return [(link[0], link[2]) for link in self.get_links()]
315
316 - def dump(self):
317 for link in self.links: 318 print '%s:%s => %s:%s' % tuple(link)
319
320 -def parse_plug(arg):
321 plugargs = arg.split(',') 322 plug = plugargs.pop(0)[1:] 323 props = {} 324 for arg in plugargs: 325 prop = arg[:arg.index('=')] 326 val = arg[arg.index('=')+1:] 327 if not prop or not val: 328 err('Invalid plug property setting: %s' % arg) 329 props[prop] = val 330 return plug, props
331
332 -def parse_prop(arg):
333 prop = arg[:arg.index('=')] 334 val = arg[arg.index('=')+1:] 335 if not prop or not val: 336 err('Invalid property setting: %s' % arg) 337 return prop, val
338
339 -def parse_arg(arg, components, linker):
340 def assert_in_component(msg): 341 if linker.pending() or not components: 342 err('Invalid grammar: %s' % msg)
343 344 if arg == '!': 345 if not components: 346 err('Invalid grammar: `!\' without feeder component') 347 linker.link() 348 349 elif arg[0] == '/': 350 assert_in_component('Plug %s does not follow a component' % arg) 351 plug, props = parse_plug(arg) 352 components.add_plug_to_current(plug, props) 353 354 elif arg.find('=') != -1: 355 assert_in_component('Property %s does not follow a component' % arg) 356 prop, val = parse_prop(arg) 357 components.add_prop_to_current(prop, val) 358 359 elif arg.find('.') != -1: 360 t = arg.split('.') 361 if len(t) != 2: 362 err('Invalid grammar: bad eater/feeder specification: %s' % arg) 363 t = [z or None for z in t] 364 if linker.pending(): 365 linker.link(eatercompname=t[0], eater=t[1]) 366 elif components: 367 linker.link(feedercompname=t[0] or components.last(), feeder=t[1]) 368 else: 369 err('Invalid grammar: trying to link from feeder %s but ' 370 'no feeder component' % arg) 371 372 else: 373 components.add(arg) 374 if linker.pending(): 375 linker.link(eatercompname=components.last()) 376
377 -def parse_args(args):
378 """Parse flumotion-launch arguments. 379 380 Parse flumotion-launch arguments, returning a list of component 381 configs. 382 383 A component config is what we will pass to a component when we 384 create it. It is a dict: 385 386 - 'name': component name 387 - 'type': component type 388 - 'properties': dict of property name => property value 389 - 'feed': list of [feeder name,...] 390 - 'source': list of [feeder name,...], (optional) 391 - 'clock-master': clock master or None 392 - 'plugs': dict of socket name => plug config 393 """ 394 395 if not args: 396 err('Usage: flumotion-launch COMPONENT [! COMPONENT]...') 397 398 components = ComponentStore() 399 400 linker = Linker(components.last) 401 402 args.reverse() # so we can pop from the tail 403 while args: 404 parse_arg(args.pop().strip(), components, linker) 405 406 feeders = linker.resolve_links(dict([(name, components[name].type) 407 for name in components])) 408 409 for compname in feeders: 410 components[compname].source = feeders[compname] 411 components.complete_and_verify_configs() 412 413 return components.sorted_configs(linker.get_sort_order())
414