1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """parsing of registry, which holds component and bundle information
23 """
24
25 import os
26 import sets
27 import stat
28 import errno
29 import sys
30 from StringIO import StringIO
31
32 from xml.sax import saxutils
33 from twisted.spread import pb
34
35 from flumotion.common import common, log, errors, fxml
36 from flumotion.common.python import makedirs
37 from flumotion.common.bundle import BundlerBasket, MergedBundler
38 from flumotion.configure import configure
39
40 __all__ = ['ComponentRegistry', 'registry']
41 __version__ = "$Rev: 6982 $"
42
43
44 READ_CACHE = False
45
46 _VALID_WIZARD_COMPONENT_TYPES = [
47 'audio-producer',
48 'video-producer',
49 'muxer',
50 'audio-encoder',
51 'video-encoder',
52 ]
53
54 _VALID_WIZARD_PLUG_TYPES = [
55 'http-consumer',
56 ]
57
59 return os.stat(file)[stat.ST_MTIME]
60
62 """
63 I represent a <component> entry in the registry
64 """
65
66
67 __pychecker__ = 'maxargs=15'
68
69 - def __init__(self, filename, type,
70 source, description, base, properties, files,
71 entries, eaters, feeders, needs_sync, clock_priority,
72 sockets, wizards):
73 """
74 @param filename: name of the XML file this component is parsed from
75 @type filename: str
76 @param properties: dict of name -> property
77 @type properties: dict of str -> L{RegistryEntryProperty}
78 @param files: list of files
79 @type files: list of L{RegistryEntryFile}
80 @param entries: dict of entry point type -> entry
81 @type entries: dict of str -> L{RegistryEntryEntry}
82 @param sockets: list of sockets supported by the element
83 @type sockets: list of str
84 @param wizards: list of wizard entries
85 @type wizards: list of L{RegistryEntryWizard}
86 """
87 self.filename = filename
88 self.type = type
89 self.source = source
90 self.description = description
91
92 if not self.description:
93 self.description = ""
94 self.base = base
95 self.properties = properties
96 self.files = files
97 self.entries = entries
98 self.eaters = eaters
99 self.feeders = feeders
100 self.needs_sync = needs_sync
101 self.clock_priority = clock_priority
102 self.sockets = sockets
103 self.wizards = wizards
104
105 - def getProperties(self):
106 """
107 Get a list of all properties.
108
109 @rtype: list of L{RegistryEntryProperty}
110 """
111 return self.properties.values()
112
113 - def hasProperty(self, name):
114 """
115 Check if the component has a property with the given name.
116 """
117 return name in self.properties.keys()
118
119 - def getFiles(self):
120 """
121 @rtype: list of L{RegistryEntryFile}
122 """
123 return self.files
124
125 - def getEntries(self):
126 return self.entries.values()
127
128 - def getEntryByType(self, type):
129 """
130 Get the entry point for the given type of entry.
131
132 @type type: string
133 """
134 return self.entries[type]
135
136 - def getGUIEntry(self):
137 if not self.files:
138 return
139
140
141 if len(self.files) > 1:
142 return
143
144 return self.files[0].getFilename()
145
148
151
152 - def getDescription(self):
153 return self.description
154
155 - def getSource(self):
157
158 - def getEaters(self):
160
161 - def getFeeders(self):
163
165 return self.needs_sync
166
168 return self.clock_priority
169
170 - def getSockets(self):
172
174 """
175 I represent a <plug> entry in the registry
176 """
177
178 - def __init__(self, filename, type, socket, entries, properties, wizards):
179 """
180 @param filename: name of the XML file this plug is parsed from
181 @type filename: str
182 @param type: the type of plug
183 @type type: str
184 @param socket: the fully qualified class name of the socket this
185 plug can be plugged in to
186 @type socket: str
187 @param entries: entry pointes for instantiating the plug
188 @type entries: list of L{RegistryEntryEntry}
189 @param properties: properties of the plug
190 @type properties: dict of str -> L{RegistryEntryProperty}
191 @param wizards: list of wizard entries
192 @type wizards: list of L{RegistryEntryWizard}
193 """
194 self.filename = filename
195 self.type = type
196 self.socket = socket
197 self.entries = entries
198 self.properties = properties
199 self.wizards = wizards
200
201 - def getProperties(self):
202 """
203 Get a list of all properties.
204
205 @rtype: list of L{RegistryEntryProperty}
206 """
207 return self.properties.values()
208
209 - def hasProperty(self, name):
210 """
211 Check if the component has a property with the given name.
212 """
213 return name in self.properties.keys()
214
215 - def getEntryByType(self, type):
216 """
217 Get the entry point for the given type of entry.
218
219 @type type: string
220 """
221 return self.entries[type]
222
223 - def getEntry(self):
224 return self.entries['default']
225
226 - def getEntries(self):
227 return self.entries.values()
228
231
232 - def getSocket(self):
234
236 "This class represents a <bundle> entry in the registry"
237 - def __init__(self, name, project, under, dependencies, directories):
238 self.name = name
239 self.project = project
240 self.under = under
241 self.dependencies = dependencies
242 self.directories = directories
243
244 - def __repr__(self):
245 return '<Bundle name=%s>' % self.name
246
249
250 - def getDependencies(self):
251 """
252 @rtype: list of str
253 """
254 return self.dependencies
255
256 - def getDirectories(self):
257 """
258 @rtype: list of L{RegistryEntryBundleDirectory}
259 """
260 return self.directories
261
262 - def getProject(self):
264
265 - def getUnder(self):
267
268 - def getBaseDir(self):
269 if self.project == configure.PACKAGE:
270 return getattr(configure, self.under)
271
272 from flumotion.project import project
273 return project.get(self.project, self.under)
274
276 "This class represents a <directory> entry in the registry"
277 - def __init__(self, name, files):
278 self.name = name
279 self.files = files
280
283
284 - def getFiles(self):
286
288 "This class represents a <filename> entry in the registry"
289 - def __init__(self, location, relative):
290 self.location = location
291 self.relative = relative
292
293 - def getLocation(self):
295
296 - def getRelative(self):
298
300 "This class represents a <property> entry in the registry"
301 - def __init__(self, name, type, description, required=False, multiple=False):
302 self.name = name
303 self.type = type
304 self.description = description
305
306 if not self.description:
307 self.description = ""
308 self.required = required
309 self.multiple = multiple
310
311 - def __repr__(self):
312 return '<Property name=%s>' % self.name
313
316
319
320 - def getDescription(self):
321 return self.description
322
323 - def isRequired(self):
325
326 - def isMultiple(self):
328
329 -class RegistryEntryCompoundProperty(RegistryEntryProperty):
330 "This class represents a <compound-property> entry in the registry"
331 - def __init__(self, name, description, properties, required=False,
332 multiple=False):
333 RegistryEntryProperty.__init__(self, name, 'compound', description,
334 required, multiple)
335 self.properties = properties
336
337 - def __repr__(self):
338 return '<Compound-property name=%s>' % self.name
339
340 - def getProperties(self):
341 """
342 Get a list of all sub-properties.
343
344 @rtype: list of L{RegistryEntryProperty}
345 """
346 return self.properties.values()
347
348 - def hasProperty(self, name):
349 """
350 Check if the compound-property has a sub-property with the
351 given name.
352 """
353 return name in self.properties
354
356 "This class represents a <file> entry in the registry"
357 - def __init__(self, filename, type):
358 self.filename = filename
359 self.type = type
360
362 return os.path.basename(self.filename)
363
366
367 - def getFilename(self):
369
370 - def isType(self, type):
371 return self.type == type
372
374 "This class represents a <entry> entry in the registry"
375 - def __init__(self, type, location, function):
376 self.type = type
377 self.location = location
378 self.function = function
379
382
383 - def getLocation(self):
385
386 - def getModuleName(self, base=None):
387 if base:
388 path = os.path.join(base, self.getLocation())
389 else:
390 path = self.getLocation()
391 return common.pathToModuleName(path)
392
393 - def getFunction(self):
395
397 "This class represents a <eater> entry in the registry"
398 - def __init__(self, name, required=True, multiple=False):
399 self.name = name
400 self.required = required
401 self.multiple = multiple
402
405
406 - def getRequired(self):
408
409 - def getMultiple(self):
411
412 -class RegistryEntryWizard(pb.Copyable):
413 "This class represents a <wizard> entry in the registry"
414 - def __init__(self, componentType, type, description, feeder,
415 eater, accepts, provides):
416 self.componentType = componentType
417 self.type = type
418 self.description = description
419 self.feeder = feeder
420 self.eater = eater
421 self.accepts = accepts
422 self.provides = provides
423
424 - def __repr__(self):
425 return '<wizard %s type=%s, feeder=%s>' % (self.componentType,
426 self.type, self.feeder)
427
428
430 """
431 This class represents an <accept-format> or <provide-format>
432 entry in the registry
433 """
435 self.media_type = media_type
436
437
439 """
440 Registry parser
441
442 I have two modes, one to parse registries and another one to parse
443 standalone component files.
444
445 For parsing registries use the parseRegistry function and for components
446 use parseRegistryFile.
447
448 I also have a list of all components and directories which the
449 registry uses (instead of saving its own copy)
450 """
451
454
456 self._components = {}
457 self._directories = {}
458 self._bundles = {}
459 self._plugs = {}
460
462 return self._components.values()
463
470
472 return self._plugs.values()
473
480
482
483
484
485
486 components = {}
487 def addComponent(comp):
488 components[comp.getType()] = comp
489
490 parsers = {'component': (self._parseComponent, addComponent)}
491 self.parseFromTable(node, parsers)
492
493 return components
494
496
497
498
499
500
501
502
503
504
505
506
507 componentType, baseDir, description = self.parseAttributes(node,
508 required=('type', 'base'), optional=('description',))
509
510 files = []
511 source = fxml.Box(None)
512 entries = {}
513 eaters = []
514 feeders = []
515 synchronization = fxml.Box((False, 100))
516 sockets = []
517 properties = {}
518 wizards = []
519
520
521
522
523
524
525
526
527 parsers = {
528 'source': (self._parseSource, source.set),
529 'properties': (self._parseProperties, properties.update),
530 'files': (self._parseFiles, files.extend),
531 'entries': (self._parseEntries, entries.update),
532 'eater': (self._parseEater, eaters.append),
533 'feeder': (self._parseFeeder, feeders.append),
534 'synchronization': (self._parseSynchronization,
535 synchronization.set),
536 'sockets': (self._parseSockets, sockets.extend),
537 'wizard': (self._parseComponentWizard, wizards.append),
538 }
539 self.parseFromTable(node, parsers)
540
541 source = source.unbox()
542 needs_sync, clock_priority = synchronization.unbox()
543
544 return RegistryEntryComponent(self.filename,
545 componentType, source, description,
546 baseDir, properties, files,
547 entries, eaters, feeders,
548 needs_sync, clock_priority,
549 sockets, wizards)
550
555
557
558
559
560 attrs = self.parseAttributes(node, required=('name', 'type'),
561 optional=('required', 'multiple', 'description'))
562 name, propertyType, required, multiple, description = attrs
563 required = common.strToBool(required)
564 multiple = common.strToBool(multiple)
565 return RegistryEntryProperty(name, propertyType, description,
566 required=required, multiple=multiple)
567
584
585 parsers = {'property': (self._parseProperty, addProperty),
586 'compound-property': (self._parseCompoundProperty,
587 addProperty)}
588 self.parseFromTable(node, parsers)
589
590 return RegistryEntryCompoundProperty(name, description, properties,
591 required=required, multiple=multiple)
592
602
603 parsers = {'property': (self._parseProperty, addProperty),
604 'compound-property': (self._parseCompoundProperty,
605 addProperty)}
606
607 self.parseFromTable(node, parsers)
608
609 return properties
610
619
631
638
650
651 - def _parseEntry(self, node):
652 attrs = self.parseAttributes(node, ('type', 'location', 'function'))
653 entryType, location, function = attrs
654 return RegistryEntryEntry(entryType, location, function)
655
657
658
659
660
661
662 entries = {}
663 def addEntry(entry):
664 if entry.getType() in entries:
665 raise fxml.ParserError("entry %s already specified"
666 % entry.getType())
667 entries[entry.getType()] = entry
668
669 parsers = {'entry': (self._parseEntry, addEntry)}
670
671 self.parseFromTable(node, parsers)
672
673 return entries
674
684
689
697
698 - def _parsePlugEntry(self, node):
699 attrs = self.parseAttributes(node, ('location', 'function'), ('type',))
700 location, function, entryType = attrs
701 if not entryType:
702 entryType = 'default'
703 return RegistryEntryEntry(entryType, location, function)
704
706 return {'default': self._parsePlugEntry(node)}
707
709
710
711
712
713
714 entries = {}
715 def addEntry(entry):
716 if entry.getType() in entries:
717 raise fxml.ParserError("entry %s already specified"
718 % entry.getType())
719 entries[entry.getType()] = entry
720
721 parsers = {'entry': (self._parsePlugEntry, addEntry)}
722
723 self.parseFromTable(node, parsers)
724
725 return entries
726
728
729
730
731
732
733
734
735 plugType, socket = self.parseAttributes(node, ('type', 'socket'))
736
737 entries = {}
738 properties = {}
739 wizards = []
740
741 parsers = {
742 'entries': (self._parsePlugEntries, entries.update),
743
744 'entry': (self._parseDefaultPlugEntry, entries.update),
745 'properties': (self._parseProperties, properties.update),
746 'wizard': (self._parsePlugWizard, wizards.append),
747 }
748
749 self.parseFromTable(node, parsers)
750
751 if not 'default' in entries:
752 raise fxml.ParserError(
753 "<plug> %s needs a default <entry>" % plugType)
754
755 return RegistryEntryPlug(self.filename, plugType,
756 socket, entries, properties,
757 wizards)
758
769
770 parsers = {'plug': (self._parsePlug, addPlug)}
771 self.parseFromTable(node, parsers)
772
773 return plugs
774
775
777 """
778 @param file: The file to parse, either as an open file object,
779 or as the name of a file to open.
780 @type file: str or file.
781 """
782 if isinstance(file, basestring):
783 self.filename = file
784 else:
785 self.filename = getattr(file, 'name', '<string>')
786 root = self.getRoot(file)
787 node = root.documentElement
788
789 if node.nodeName != 'registry':
790
791
792 self.debug('%s does not have registry as root tag' % self.filename)
793 return
794
795
796 self._parseRoot(node, disallowed=['directories'])
797 root.unlink()
798
807
808 parsers = {'bundle': (self._parseBundle, addBundle)}
809 self.parseFromTable(node, parsers)
810
811 return bundles
812
814
815
816
817
818
819 attrs = self.parseAttributes(node, ('name',), ('project', 'under'))
820 name, project, under = attrs
821 project = project or configure.PACKAGE
822 under = under or 'pythondir'
823
824 dependencies = []
825 directories = []
826
827 parsers = {'dependencies': (self._parseBundleDependencies,
828 dependencies.extend),
829 'directories': (self._parseBundleDirectories,
830 directories.extend)}
831 self.parseFromTable(node, parsers)
832
833 return RegistryEntryBundle(name, project, under, dependencies, directories)
834
838
850
862