Package flumotion :: Package common :: Module registry
[hide private]

Source Code for Module flumotion.common.registry

   1  # -*- Mode: Python; test-case-name: flumotion.test.test_registry -*- 
   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  parsing of registry, which holds component and bundle information 
  24  """ 
  25   
  26  import os 
  27  import sets 
  28  import stat 
  29  import errno 
  30  from StringIO import StringIO 
  31   
  32  from xml.dom import minidom, Node 
  33  from xml.parsers import expat 
  34  from xml.sax import saxutils 
  35   
  36  from flumotion.common import common, log, package, bundle, errors, fxml 
  37  from flumotion.configure import configure 
  38   
  39  # Re-enable when reading the registry cache is lighter-weight, or we 
  40  # decide that it's a good idea, or something. See #799. 
  41  READ_CACHE = False 
  42   
  43  __all__ = ['ComponentRegistry', 'registry'] 
  44   
45 -def _getMTime(file):
46 return os.stat(file)[stat.ST_MTIME]
47
48 -class RegistryEntryComponent:
49 """ 50 I represent a <component> entry in the registry 51 """ 52 # RegistryEntryComponent has a constructor with a lot of arguments, 53 # but that's ok here. Allow it through pychecker. 54 __pychecker__ = 'maxargs=14' 55
56 - def __init__(self, filename, type, 57 source, description, base, properties, files, 58 entries, eaters, feeders, needs_sync, clock_priority, 59 sockets):
60 """ 61 @param filename: name of the XML file this component is parsed from 62 @type filename: str 63 @param properties: dict of name -> property 64 @type properties: dict of str -> L{RegistryEntryProperty} 65 @param entries: dict of entry point type -> entry 66 @type entries: dict of str -> L{RegistryEntryEntry} 67 @param sockets: list of sockets supported by the element 68 @type sockets: list of str 69 """ 70 self.filename = filename 71 self.type = type 72 self.source = source 73 self.description = description 74 # we don't want to end up with the string "None" 75 if not self.description: 76 self.description = "" 77 self.base = base 78 self.properties = properties 79 self.files = files 80 self.entries = entries 81 self.eaters = eaters 82 self.feeders = feeders 83 self.needs_sync = needs_sync 84 self.clock_priority = clock_priority 85 self.sockets = sockets
86
87 - def getProperties(self):
88 """ 89 Get a list of all properties. 90 91 @rtype: list of L{RegistryEntryProperty} 92 """ 93 return self.properties.values()
94
95 - def hasProperty(self, name):
96 """ 97 Check if the component has a property with the given name. 98 """ 99 return name in self.properties.keys()
100
101 - def getFiles(self):
102 return self.files
103
104 - def getEntries(self):
105 return self.entries.values()
106
107 - def getEntryByType(self, type):
108 """ 109 Get the entry point for the given type of entry. 110 111 @type type: string 112 """ 113 return self.entries[type]
114
115 - def getGUIEntry(self):
116 if not self.files: 117 return 118 119 # FIXME: Handle multiple files 120 if len(self.files) > 1: 121 return 122 123 return self.files[0].getFilename()
124
125 - def getType(self):
126 return self.type
127
128 - def getBase(self):
129 return self.base
130
131 - def getDescription(self):
132 return self.description
133
134 - def getSource(self):
135 return self.source
136
137 - def getEaters(self):
138 return self.eaters
139
140 - def getFeeders(self):
141 return self.feeders
142
143 - def getNeedsSynchronization(self):
144 return self.needs_sync
145
146 - def getClockPriority(self):
147 return self.clock_priority
148
149 - def getSockets(self):
150 return self.sockets
151
152 -class RegistryEntryPlug:
153 """ 154 I represent a <plug> entry in the registry 155 """ 156
157 - def __init__(self, filename, type, socket, entry, properties):
158 """ 159 @param filename: name of the XML file this plug is parsed from 160 @type filename: str 161 @param type: the type of plug 162 @type type: str 163 @param socket: the fully qualified class name of the socket this 164 plug can be plugged in to 165 @type socket: str 166 @param entry: entry point for instantiating the plug 167 @type entry: L{RegistryEntryEntry} 168 @param properties: properties of the plug 169 @type properties: dict of str -> L{RegistryEntryProperty} 170 """ 171 self.filename = filename 172 self.type = type 173 self.socket = socket 174 self.entry = entry 175 self.properties = properties
176
177 - def getProperties(self):
178 """ 179 Get a list of all properties. 180 181 @rtype: list of L{RegistryEntryProperty} 182 """ 183 return self.properties.values()
184
185 - def hasProperty(self, name):
186 """ 187 Check if the component has a property with the given name. 188 """ 189 return name in self.properties.keys()
190
191 - def getEntry(self):
192 return self.entry
193
194 - def getType(self):
195 return self.type
196
197 - def getSocket(self):
198 return self.socket
199
200 -class RegistryEntryBundle:
201 "This class represents a <bundle> entry in the registry"
202 - def __init__(self, name, project, under, dependencies, directories):
203 self.name = name 204 self.project = project 205 self.under = under 206 self.dependencies = dependencies 207 self.directories = directories
208
209 - def __repr__(self):
210 return '<Bundle name=%s>' % self.name
211
212 - def getName(self):
213 return self.name
214
215 - def getDependencies(self):
216 return self.dependencies
217
218 - def getDirectories(self):
219 return self.directories
220
221 - def getProject(self):
222 return self.project
223
224 - def getUnder(self):
225 return self.under
226
227 - def getBaseDir(self):
228 if self.project == 'flumotion': 229 return getattr(configure, self.under) 230 231 from flumotion.project import project 232 return project.get(self.project, self.under)
233
234 -class RegistryEntryBundleDirectory:
235 "This class represents a <directory> entry in the registry"
236 - def __init__(self, name, files):
237 self.name = name 238 self.files = files
239
240 - def getName(self):
241 return self.name
242
243 - def getFiles(self):
244 return self.files
245
246 -class RegistryEntryBundleFilename:
247 "This class represents a <filename> entry in the registry"
248 - def __init__(self, location, relative):
249 self.location = location 250 self.relative = relative
251
252 - def getLocation(self):
253 return self.location
254
255 - def getRelative(self):
256 return self.relative
257
258 -class RegistryEntryProperty:
259 "This class represents a <property> entry in the registry"
260 - def __init__(self, name, type, description, required=False, multiple=False):
261 self.name = name 262 self.type = type 263 self.description = description 264 # we don't want to end up with the string "None" 265 if not self.description: 266 self.description = "" 267 self.required = required 268 self.multiple = multiple
269
270 - def __repr__(self):
271 return '<Property name=%s>' % self.name
272
273 - def getName(self):
274 return self.name
275
276 - def getType(self):
277 return self.type
278
279 - def getDescription(self):
280 return self.description
281
282 - def isRequired(self):
283 return self.required
284
285 - def isMultiple(self):
286 return self.multiple
287
288 -class RegistryEntryCompoundProperty(RegistryEntryProperty):
289 "This class represents a <compound-property> entry in the registry"
290 - def __init__(self, name, description, properties, required=False, 291 multiple=False):
292 RegistryEntryProperty.__init__(self, name, 'compound', description, 293 required, multiple) 294 self.properties = properties
295
296 - def __repr__(self):
297 return '<Compound-property name=%s>' % self.name
298
299 - def getProperties(self):
300 """ 301 Get a list of all sub-properties. 302 303 @rtype: list of L{RegistryEntryProperty} 304 """ 305 return self.properties.values()
306
307 - def hasProperty(self, name):
308 """ 309 Check if the compound-property has a sub-property with the 310 given name. 311 """ 312 return name in self.properties
313
314 -class RegistryEntryFile:
315 "This class represents a <file> entry in the registry"
316 - def __init__(self, filename, type):
317 self.filename = filename 318 self.type = type
319
320 - def getName(self):
321 return os.path.basename(self.filename)
322
323 - def getType(self):
324 return self.type
325
326 - def getFilename(self):
327 return self.filename
328
329 - def isType(self, type):
330 return self.type == type
331
332 -class RegistryEntryEntry:
333 "This class represents a <entry> entry in the registry"
334 - def __init__(self, type, location, function):
335 self.type = type 336 self.location = location 337 self.function = function
338
339 - def getType(self):
340 return self.type
341
342 - def getLocation(self):
343 return self.location
344
345 - def getModuleName(self, base=None):
346 if base: 347 path = os.path.join(base, self.getLocation()) 348 else: 349 path = self.getLocation() 350 return common.pathToModuleName(path)
351
352 - def getFunction(self):
353 return self.function
354
355 -class RegistryEntryEater:
356 "This class represents a <eater> entry in the registry"
357 - def __init__(self, name, required=True, multiple=False):
358 self.name = name 359 self.required = required 360 self.multiple = multiple
361
362 - def getName(self):
363 return self.name
364
365 - def getRequired(self):
366 return self.required
367
368 - def getMultiple(self):
369 return self.multiple
370
371 -class RegistryParser(fxml.Parser):
372 """ 373 Registry parser 374 375 I have two modes, one to parse registries and another one to parse 376 standalone component files. 377 378 For parsing registries use the parseRegistry function and for components 379 use parseRegistryFile. 380 381 I also have a list of all components and directories which the 382 registry uses (instead of saving its own copy) 383 """ 384
385 - def __init__(self):
386 self.clean()
387
388 - def clean(self):
389 self._components = {} 390 self._directories = {} # path -> RegistryDirectory 391 self._bundles = {} 392 self._plugs = {}
393
394 - def getComponents(self):
395 return self._components.values()
396
397 - def getComponent(self, name):
398 try: 399 return self._components[name] 400 except KeyError: 401 raise errors.UnknownComponentError("unknown component type:" 402 " %s" % (name,))
403
404 - def getPlugs(self):
405 return self._plugs.values()
406
407 - def getPlug(self, name):
408 try: 409 return self._plugs[name] 410 except KeyError: 411 raise errors.UnknownPlugError("unknown plug type: %s" 412 % (name,))
413
414 - def _parseComponents(self, node):
415 # <components> 416 # <component> 417 # </components> 418 419 components = {} 420 def addComponent(comp): 421 components[comp.getType()] = comp
422 423 parsers = {'component': (self._parseComponent, addComponent)} 424 self.parseFromTable(node, parsers) 425 426 return components
427
428 - def _parseComponent(self, node):
429 # <component type="..." base="..." description="..."> 430 # <source> 431 # <eater> 432 # <feeder> 433 # <properties> 434 # <entries> 435 # <synchronization> 436 # <sockets> 437 # </component> 438 439 type, baseDir, description = self.parseAttributes(node, 440 required=('type', 'base'), optional=('description',)) 441 442 files = [] 443 source = fxml.Box(None) 444 entries = {} 445 eaters = [] 446 feeders = [] 447 synchronization = fxml.Box((False, 100)) 448 sockets = [] 449 properties = {} 450 451 # Merge in options for inherit 452 #if node.hasAttribute('inherit'): 453 # base_type = str(node.getAttribute('inherit')) 454 # base = self.getComponent(base_type) 455 # for prop in base.getProperties(): 456 # properties[prop.getName()] = prop 457 458 parsers = { 459 'source': (self._parseSource, source.set), 460 'properties': (self._parseProperties, properties.update), 461 'files': (self._parseFiles, files.extend), 462 'entries': (self._parseEntries, entries.update), 463 'eater': (self._parseEater, eaters.append), 464 'feeder': (self._parseFeeder, feeders.append), 465 'synchronization': (self._parseSynchronization, 466 synchronization.set), 467 'sockets': (self._parseSockets, sockets.extend), 468 } 469 470 self.parseFromTable(node, parsers) 471 472 source = source.unbox() 473 needs_sync, clock_priority = synchronization.unbox() 474 475 return RegistryEntryComponent(self.filename, 476 type, source, description, baseDir, 477 properties, files, 478 entries, eaters, feeders, 479 needs_sync, clock_priority, 480 sockets)
481
482 - def _parseSource(self, node):
483 # <source location="..."/> 484 location, = self.parseAttributes(node, ('location',)) 485 return location
486
487 - def _parseProperty(self, node):
488 # <property name="..." type="" required="yes/no" multiple="yes/no"/> 489 # returns: RegistryEntryProperty 490 491 attrs = self.parseAttributes(node, required=('name', 'type'), 492 optional=('required', 'multiple', 'description')) 493 name, type, required, multiple, description = attrs 494 required = common.strToBool(required) 495 multiple = common.strToBool(multiple) 496 return RegistryEntryProperty(name, type, description, required=required, 497 multiple=multiple)
498
499 - def _parseCompoundProperty(self, node):
500 # <compound-property name="..." required="yes/no" multiple="yes/no"> 501 # <property ... />* 502 # <compound-property ... >...</compound-property>* 503 # </compound-property> 504 # returns: RegistryEntryCompoundProperty 505 506 attrs = self.parseAttributes(node, required=('name',), 507 optional=('required', 'multiple', 'description')) 508 name, required, multiple, description = attrs 509 required = common.strToBool(required) 510 multiple = common.strToBool(multiple) 511 512 properties = {} 513 def addProperty(prop): 514 properties[prop.getName()] = prop
515 516 parsers = {'property': (self._parseProperty, addProperty), 517 'compound-property': (self._parseCompoundProperty, 518 addProperty)} 519 self.parseFromTable(node, parsers) 520 521 return RegistryEntryCompoundProperty(name, description, properties, 522 required=required, multiple=multiple) 523
524 - def _parseProperties(self, node):
525 # <properties> 526 # <property>* 527 # <compound-proerty>* 528 # </properties> 529 530 properties = {} 531 def addProperty(prop): 532 properties[prop.getName()] = prop
533 534 parsers = {'property': (self._parseProperty, addProperty), 535 'compound-property': (self._parseCompoundProperty, 536 addProperty)} 537 538 self.parseFromTable(node, parsers) 539 540 return properties 541
542 - def _parseFile(self, node):
543 # <file name="..." type=""/> 544 # returns: RegistryEntryFile 545 546 name, type = self.parseAttributes(node, ('name', 'type')) 547 dir = os.path.split(self.filename)[0] 548 filename = os.path.join(dir, name) 549 return RegistryEntryFile(filename, type)
550
551 - def _parseFiles(self, node):
552 # <files> 553 # <file> 554 # </files> 555 556 files = [] 557 parsers = {'file': (self._parseFile, files.append)} 558 559 self.parseFromTable(node, parsers) 560 561 return files
562
563 - def _parseSocket(self, node):
564 # <socket type=""/> 565 # returns: str of the type 566 567 type, = self.parseAttributes(node, ('type',)) 568 return type
569
570 - def _parseSockets(self, node):
571 # <sockets> 572 # <socket> 573 # </sockets> 574 575 sockets = [] 576 parsers = {'socket': (self._parseSocket, sockets.append)} 577 578 self.parseFromTable(node, parsers) 579 580 return sockets
581
582 - def _parseEntry(self, node):
583 attrs = self.parseAttributes(node, ('type', 'location', 'function')) 584 type, location, function = attrs 585 return RegistryEntryEntry(type, location, function)
586
587 - def _parseEntries(self, node):
588 # <entries> 589 # <entry> 590 # </entries> 591 # returns: dict of type -> entry 592 593 entries = {} 594 def addEntry(entry): 595 if entry.getType() in entries: 596 raise fxml.ParserError("entry %s already specified" 597 % entry.getType()) 598 entries[entry.getType()] = entry
599 600 parsers = {'entry': (self._parseEntry, addEntry)} 601 602 self.parseFromTable(node, parsers) 603 604 return entries 605
606 - def _parseEater(self, node):
607 # <eater name="..." [required="yes/no"] [multiple="yes/no"]/> 608 attrs = self.parseAttributes(node, ('name',), ('required', 'multiple')) 609 name, required, multiple = attrs 610 # only required defaults to True 611 required = common.strToBool(required or 'True') 612 multiple = common.strToBool(multiple) 613 614 return RegistryEntryEater(name, required, multiple)
615
616 - def _parseFeeder(self, node):
617 # <feeder name="..."/> 618 name, = self.parseAttributes(node, ('name',)) 619 return name
620
621 - def _parseSynchronization(self, node):
622 # <synchronization [required="yes/no"] [clock-priority="100"]/> 623 attrs = self.parseAttributes(node, (), ('required', 'clock-priority')) 624 required, clock_priority = attrs 625 required = common.strToBool(required) 626 clock_priority = int(clock_priority or '100') 627 return required, clock_priority
628
629 - def _parsePlugEntry(self, node):
630 # <entry location="" function=""/> 631 # returns: RegistryEntryEntry 632 633 attrs = self.parseAttributes(node, ('location', 'function')) 634 location, function = attrs 635 return RegistryEntryEntry('plug', location, function)
636
637 - def _parsePlug(self, node):
638 # <plug socket="..." type="..."> 639 # <entry> 640 # <properties> 641 # </plug> 642 643 type, socket = self.parseAttributes(node, ('type', 'socket')) 644 645 entry = fxml.Box(None) 646 properties = {} 647 648 parsers = {'entry': (self._parsePlugEntry, entry.set), 649 'properties': (self._parseProperties, properties.update)} 650 651 self.parseFromTable(node, parsers) 652 653 if not entry.unbox(): 654 raise fxml.ParserError("<plug> %s needs an <entry>" % type) 655 656 return RegistryEntryPlug(self.filename, type, 657 socket, entry.unbox(), properties)
658
659 - def _parsePlugs(self, node):
660 # <plugs> 661 # <plug> 662 # </plugs> 663 664 self.checkAttributes(node) 665 666 plugs = {} 667 def addPlug(plug): 668 plugs[plug.getType()] = plug
669 670 parsers = {'plug': (self._parsePlug, addPlug)} 671 self.parseFromTable(node, parsers) 672 673 return plugs 674 675 ## Component registry specific functions
676 - def parseRegistryFile(self, file):
677 """ 678 @param file: The file to parse, either as an open file object, 679 or as the name of a file to open. 680 @type file: str or file. 681 """ 682 self.filename = getattr(file, 'name', '<string>') 683 root = self.getRoot(file) 684 node = root.documentElement 685 686 if node.nodeName != 'registry': 687 # ignore silently, since this function is used to parse all 688 # .xml files encountered 689 self.debug('%s does not have registry as root tag' % self.filename) 690 return 691 692 # shouldn't have <directories> elements in registry fragments 693 self._parseRoot(node, disallowed=['directories']) 694 root.unlink()
695
696 - def _parseBundles(self, node):
697 # <bundles> 698 # <bundle> 699 # </bundles> 700 701 bundles = {} 702 def addBundle(bundle): 703 bundles[bundle.getName()] = bundle
704 705 parsers = {'bundle': (self._parseBundle, addBundle)} 706 self.parseFromTable(node, parsers) 707 708 return bundles 709
710 - def _parseBundle(self, node):
711 # <bundle name="..."> 712 # <dependencies> 713 # <directories> 714 # </bundle> 715 716 attrs = self.parseAttributes(node, ('name',), ('project', 'under')) 717 name, project, under = attrs 718 project = project or 'flumotion' 719 under = under or 'pythondir' 720 721 dependencies = [] 722 directories = [] 723 724 parsers = {'dependencies': (self._parseBundleDependencies, 725 dependencies.extend), 726 'directories': (self._parseBundleDirectories, 727 directories.extend)} 728 self.parseFromTable(node, parsers) 729 730 return RegistryEntryBundle(name, project, under, dependencies, directories)
731
732 - def _parseBundleDependency(self, node):
733 name, = self.parseAttributes(node, ('name',)) 734 return name
735
736 - def _parseBundleDependencies(self, node):
737 # <dependencies> 738 # <dependency name=""> 739 # </dependencies> 740 dependencies = [] 741 742 parsers = {'dependency': (self._parseBundleDependency, 743 dependencies.append)} 744 self.parseFromTable(node, parsers) 745 746 return dependencies
747
748 - def _parseBundleDirectories(self, node):
749 # <directories> 750 # <directory> 751 # </directories> 752 directories = [] 753 754 parsers = {'directory': (self._parseBundleDirectory, 755 directories.append)} 756 self.parseFromTable(node, parsers) 757 758 return directories
759
760 - def _parseBundleDirectoryFilename(self, node, name):
761 attrs = self.parseAttributes(node, ('location',), ('relative',)) 762 location, relative = attrs 763 764 if not relative: 765 relative = os.path.join(name, location) 766 767 return RegistryEntryBundleFilename(location, relative)
768
769 - def _parseBundleDirectory(self, node):
770 # <directory name=""> 771 # <filename location="" [ relative="" ] > 772 # </directory> 773 name, = self.parseAttributes(node, ('name',)) 774 775 filenames = [] 776 def parseFilename(node): 777 return self._parseBundleDirectoryFilename(node, name)
778 779 parsers = {'filename': (parseFilename, filenames.append)} 780 self.parseFromTable(node, parsers) 781 782 return RegistryEntryBundleDirectory(name, filenames) 783 784 ## Base registry specific functions
785 - def parseRegistry(self, file):
786 """ 787 @param file: The file to parse, either as an open file object, 788 or as the name of a file to open. 789 @type file: str or file. 790 """ 791 self.filename = getattr(file, 'name', '<string>') 792 root = self.getRoot(file) 793 self._parseRoot(root.documentElement) 794 root.unlink()
795
796 - def getDirectories(self):
797 return self._directories.values()
798
799 - def getDirectory(self, name):
800 return self._directories[name]
801
802 - def addDirectory(self, directory):
803 """ 804 Add a registry path object to the parser. 805 806 @type directory: {RegistryDirectory} 807 """ 808 self._directories[directory.getPath()] = directory
809
810 - def removeDirectoryByPath(self, path):
811 """ 812 Remove a directory from the parser given the path. 813 Used when the path does not actually contain any registry information. 814 """ 815 if path in self._directories.keys(): 816 del self._directories[path]
817
818 - def _parseRoot(self, node, disallowed=None):
819 # <components>...</components>* 820 # <plugs>...</plugs>* 821 # <directories>...</directories>* 822 # <bundles>...</bundles>* 823 parsers = {'components': (self._parseComponents, 824 self._components.update), 825 'directories': (self._parseDirectories, 826 self._directories.update), 827 'bundles': (self._parseBundles, self._bundles.update), 828 'plugs': (self._parsePlugs, self._plugs.update)} 829 830 if disallowed: 831 for k in disallowed: 832 del parsers[k] 833 834 self.parseFromTable(node, parsers)
835
836 - def _parseDirectories(self, node):
837 # <directories> 838 # <directory> 839 # </directories> 840 841 directories = {} 842 def addDirectory(d): 843 directories[d.getPath()] = d
844 845 parsers = {'directory': (self._parseDirectory, addDirectory)} 846 self.parseFromTable(node, parsers) 847 848 return directories 849
850 - def _parseDirectory(self, node):
851 # <directory filename="..."/> 852 filename, = self.parseAttributes(node, ('filename',)) 853 return RegistryDirectory(filename)
854 855 856 # FIXME: filename -> path
857 -class RegistryDirectory(log.Loggable):
858 """ 859 I represent a directory under a path managed by the registry. 860 I can be queried for a list of partial registry .xml files underneath 861 the given path, under the given prefix. 862 """
863 - def __init__(self, path, prefix='flumotion'):
864 self._path = path 865 self._prefix = prefix 866 scanPath = os.path.join(path, prefix) 867 self._files, self._dirs = self._getFileLists(scanPath)
868
869 - def __repr__(self):
870 return "<RegistryDirectory %s>" % self._path
871
872 - def _getFileLists(self, root):
873 """ 874 Get all files ending in .xml from all directories under the given root. 875 876 @type root: string 877 @param root: the root directory under which to search 878 879 @returns: a list of .xml files, relative to the given root directory 880 """ 881 files = [] 882 dirs = [] 883 884 if os.path.exists(root): 885 try: 886 directory_files = os.listdir(root) 887 except OSError, e: 888 if e.errno == errno.EACCES: 889 return files, dirs 890 else: 891 raise 892 893 dirs.append(root) 894 895 for dir in directory_files: 896 filename = os.path.join(root, dir) 897 # if it's a .xml file, then add it to the list 898 if not os.path.isdir(filename): 899 if filename.endswith('.xml'): 900 files.append(filename) 901 # if it's a directory and not an svn directory, then get 902 # its files and add them 903 elif dir != '.svn': 904 newFiles, newDirs = self._getFileLists(filename) 905 files.extend(newFiles) 906 dirs.extend(newDirs) 907 908 return files, dirs
909
910 - def rebuildNeeded(self, mtime):
911 def _rebuildNeeded(file): 912 try: 913 if _getMTime(file) > mtime: 914 self.debug("Path %s changed since registry last " 915 "scanned", f) 916 return True 917 return False 918 except OSError: 919 self.debug("Failed to stat file %s, need to rescan", f) 920 return True
921 922 assert self._files, "Path %s does not have registry files" % self._path 923 for f in self._files: 924 if _rebuildNeeded(f): 925 return True 926 for f in self._dirs: 927 if _rebuildNeeded(f): 928 return True 929 return False
930
931 - def getFiles(self):
932 """ 933 Return a list of all .xml registry files underneath this registry 934 path. 935 """ 936 return self._files
937
938 - def getPath(self):
939 return self._path
940
941 -class RegistryWriter(log.Loggable):
942 - def __init__(self, components, plugs, bundles, directories):
943 """ 944 @param components: components to write 945 @type components: list of L{RegistryEntryComponent} 946 @param plugs: plugs to write 947 @type plugs: list of L{RegistryEntryPlug} 948 @param bundles: bundles to write 949 @type bundles: list of L{RegistryEntryBundle} 950 @param directories: directories to write 951 @type directories: list of L{RegistryEntryBundleDirectory} 952 """ 953 self.components = components 954 self.plugs = plugs 955 self.bundles = bundles 956 self.directories = directories
957
958 - def dump(self, fd):
959 """ 960 Dump the cache of components to the given opened file descriptor. 961 962 @type fd: integer 963 @param fd: open file descriptor to write to 964 """ 965 966 def w(i, msg): 967 print >> fd, ' '*i + msg
968 def e(attr): 969 return saxutils.quoteattr(attr)
970 971 def _dump_proplist(i, proplist, ioff=2): 972 for prop in proplist: 973 if isinstance(prop, RegistryEntryCompoundProperty): 974 _dump_compound(i, prop) 975 else: 976 w(i, ('<property name="%s" type="%s"' 977 % (prop.getName(), prop.getType()))) 978 w(i, (' description=%s' 979 % (e(prop.getDescription()),))) 980 w(i, (' required="%s" multiple="%s"/>' 981 % (prop.isRequired(), prop.isMultiple()))) 982 983 def _dump_compound(i, cprop, ioff=2): 984 w(i, ('<compound-property name="%s"' % (cprop.getName(),))) 985 w(i, (' description=%s' 986 % (e(cprop.getDescription()),))) 987 w(i, (' required="%s" multiple="%s">' 988 % (cprop.isRequired(), cprop.isMultiple()))) 989 _dump_proplist(i + ioff, cprop.getProperties()) 990 w(i, ('</compound-property>')) 991 992 w(0, '<registry>') 993 w(0, '') 994 995 # Write components 996 w(2, '<components>') 997 w(0, '') 998 for component in self.components: 999 w(4, '<component type="%s" base="%s"' % ( 1000 component.getType(), component.getBase())) 1001 w(4, ' description=%s>' 1002 % (e(component.getDescription()),)) 1003 1004 w(6, '<source location="%s"/>' % component.getSource()) 1005 for x in component.getEaters(): 1006 w(6, '<eater name="%s" required="%s" multiple="%s"/>' 1007 % (x.getName(), x.getRequired() and "yes" or "no", 1008 x.getMultiple() and "yes" or "no")) 1009 for x in component.getFeeders(): 1010 w(6, '<feeder name="%s"/>' % x) 1011 w(6, '<synchronization required="%s" clock-priority="%d"/>' 1012 % (component.getNeedsSynchronization() and "yes" or "no", 1013 component.getClockPriority())) 1014 1015 sockets = component.getSockets() 1016 if sockets: 1017 w(6, '<sockets>') 1018 for socket in sockets: 1019 w(8, '<socket type="%s"/>' % socket) 1020 w(6, '</sockets>') 1021 1022 w(6, '<properties>') 1023 _dump_proplist(8, component.getProperties()) 1024 w(6, '</properties>') 1025 1026 files = component.getFiles() 1027 if files: 1028 w(6, '<files>') 1029 for file in files: 1030 w(8, '<file name="%s" type="%s"/>' % ( 1031 file.getName(), 1032 file.getType())) 1033 w(6, '</files>') 1034 1035 entries = component.getEntries() 1036 if entries: 1037 w(6, '<entries>') 1038 for entry in entries: 1039 w(8, '<entry type="%s" location="%s" function="%s"/>' % ( 1040 entry.getType(), 1041 entry.getLocation(), 1042 entry.getFunction())) 1043 w(6, '</entries>') 1044 w(4, '</component>') 1045 w(0, '') 1046 1047 w(2, '</components>') 1048 w(0, '') 1049 1050 # Write plugs 1051 w(2, '<plugs>') 1052 w(0, '') 1053 for plug in self.plugs: 1054 w(4, '<plug type="%s" socket="%s">' 1055 % (plug.getType(), plug.getSocket())) 1056 1057 entry = plug.getEntry() 1058 w(6, ('<entry location="%s" function="%s"/>' 1059 % (entry.getLocation(), entry.getFunction()))) 1060 1061 w(6, '<properties>') 1062 _dump_proplist(8, plug.getProperties()) 1063 w(6, '</properties>') 1064 1065 w(4, '</plug>') 1066 w(0, '') 1067 1068 w(2, '</plugs>') 1069 w(0, '') 1070 1071 # bundles 1072 w(2, '<bundles>') 1073 for bundle in self.bundles: 1074 w(4, '<bundle name="%s" under="%s" project="%s">' % ( 1075 bundle.getName(), bundle.getUnder(), bundle.getProject())) 1076 1077 dependencies = bundle.getDependencies() 1078 if dependencies: 1079 w(6, '<dependencies>') 1080 for dependency in dependencies: 1081 w(8, '<dependency name="%s"/>' % dependency) 1082 w(6, '</dependencies>') 1083 1084 dirs = bundle.getDirectories() 1085 if dirs: 1086 w(6, '<directories>') 1087 for dir in dirs: 1088 w(8, '<directory name="%s">' % dir.getName()) 1089 for filename in dir.getFiles(): 1090 w(10, '<filename location="%s" relative="%s"/>' % ( 1091 filename.getLocation(), filename.getRelative())) 1092 w(8, '</directory>') 1093 w(6, '</directories>') 1094 1095 w(4, '</bundle>') 1096 w(0, '') 1097 w(2, '</bundles>') 1098 1099 1100 # Directories 1101 directories = self.directories 1102 if directories: 1103 w(2, '<directories>') 1104 w(0, '') 1105 for d in directories: 1106 w(4, '<directory filename="%s"/>' % d.getPath()) 1107 w(2, '</directories>') 1108 w(0, '') 1109 1110 w(0, '</registry>') 1111
1112 -class ComponentRegistry(log.Loggable):
1113 """Registry, this is normally not instantiated.""" 1114 1115 logCategory = 'registry' 1116 filename = os.path.join(configure.registrydir, 'registry.xml') 1117
1118 - def __init__(self):
1119 self._parser = RegistryParser() 1120 1121 if (READ_CACHE and 1122 os.path.exists(self.filename) and 1123 os.access(self.filename, os.R_OK)): 1124 self.info('Parsing registry: %s' % self.filename) 1125 try: 1126 self._parser.parseRegistry(self.filename) 1127 except fxml.ParserError, e: 1128 # this can happen for example if we upgraded to a new version, 1129 # ran, then downgraded again; the registry can then contain 1130 # XML keys that are not understood by this version. 1131 # This is non-fatal, and gets fixed due to a re-scan 1132 self.warning('Could not parse registry %s.' % self.filename) 1133 self.debug('fxml.ParserError: %s' % log.getExceptionMessage(e)) 1134 1135 self.verify(force=not READ_CACHE)
1136
1137 - def addFile(self, file):
1138 """ 1139 @param file: The file to add, either as an open file object, or 1140 as the name of a file to open. 1141 @type file: str or file. 1142 """ 1143 if isinstance(file, str) and file.endswith('registry.xml'): 1144 self.warning('%s seems to be an old registry in your tree, ' 1145 'please remove it', file) 1146 self.debug('Adding file: %r', file) 1147 self._parser.parseRegistryFile(file)
1148
1149 - def addFromString(self, string):
1150 f = StringIO(string) 1151 self.addFile(f) 1152 f.close()
1153
1154 - def addRegistryPath(self, path, prefix='flumotion'):
1155 """ 1156 Add a registry path to this registry, scanning it for registry 1157 snippets. 1158 1159 @param path: a full path containing a 'flumotion' directory, 1160 which will be scanned for registry files. 1161 1162 @rtype: bool 1163 @returns: whether the path could be added 1164 """ 1165 self.debug('path %s, prefix %s' % (path, prefix)) 1166 if not os.path.exists(path): 1167 self.warning("Cannot add non-existent path '%s' to registry" % path) 1168 return False 1169 if not os.path.exists(os.path.join(path, prefix)): 1170 self.warning("Cannot add path '%s' to registry " 1171 "since it does not contain prefix '%s'" % (path, prefix)) 1172 return False 1173 1174 # registry path was either not watched or updated, or a force was 1175 # asked, so reparse 1176 self.info('Scanning registry path %s' % path) 1177 registryPath = RegistryDirectory(path, prefix=prefix) 1178 files = registryPath.getFiles() 1179 self.debug('Found %d possible registry files' % len(files)) 1180 map(self.addFile, files) 1181 1182 self._parser.addDirectory(registryPath) 1183 return True
1184 1185 # fixme: these methods inconsistenly molest and duplicate those of 1186 # the parser.
1187 - def isEmpty(self):
1188 return len(self._parser._components) == 0
1189
1190 - def getComponent(self, name):
1191 """ 1192 @rtype: L{RegistryEntryComponent} 1193 """ 1194 return self._parser.getComponent(name)
1195
1196 - def hasComponent(self, name):
1197 return name in self._parser._components
1198
1199 - def getComponents(self):
1200 return self._parser.getComponents()
1201
1202 - def getPlug(self, type):
1203 """ 1204 @rtype: L{RegistryEntryPlug} 1205 """ 1206 return self._parser.getPlug(type)
1207
1208 - def hasPlug(self, name):
1209 return name in self._parser._plugs
1210
1211 - def getPlugs(self):
1212 return self._parser.getPlugs()
1213
1214 - def getBundles(self):
1215 return self._parser._bundles.values()
1216
1217 - def getDirectories(self):
1218 return self._parser.getDirectories()
1219
1220 - def makeBundlerBasket(self):
1221 """ 1222 @rtype: L{flumotion.common.bundle.BundlerBasket} 1223 """ 1224 def load(): 1225 ret = bundle.BundlerBasket() 1226 for b in self.getBundles(): 1227 bundleName = b.getName() 1228 self.debug('Adding bundle %s' % bundleName) 1229 for d in b.getDirectories(): 1230 directory = d.getName() 1231 for file in d.getFiles(): 1232 try: 1233 basedir = b.getBaseDir() 1234 except errors.NoProjectError, e: 1235 self.warning("Could not find project %s" % e.args) 1236 raise 1237 fullpath = os.path.join(basedir, directory, 1238 file.getLocation()) 1239 relative = file.getRelative() 1240 self.log('Adding path %s as %s to bundle %s' % ( 1241 fullpath, relative, bundleName)) 1242 try: 1243 ret.add(bundleName, fullpath, relative) 1244 except Exception, e: 1245 self.debug("Reason: %r" % e) 1246 raise RuntimeError( 1247 'Could not add %s to bundle %s (%s)' 1248 % (fullpath, bundleName, e)) 1249 for d in b.getDependencies(): 1250 self.log('Adding dependency of %s on %s' % (bundleName, d)) 1251 ret.depend(bundleName, d) 1252 return ret
1253 1254 try: 1255 return load() 1256 except Exception, e: 1257 self.warning("Bundle problem, rebuilding registry (%s)" % e) 1258 self.verify(force=True) 1259 try: 1260 return load() 1261 except Exception, e: 1262 self.debug("Could not register bundles twice: %s" % 1263 log.getExceptionMessage(e)) 1264 self.error("Could not not register bundles (%s)" % e)
1265
1266 - def dump(self, fd):
1267 """ 1268 Dump the cache of components to the given opened file descriptor. 1269 1270 @type fd: integer 1271 @param fd: open file descriptor to write to 1272 """ 1273 writer = RegistryWriter(self.getComponents(), self.getPlugs(), 1274 self.getBundles(), self.getDirectories()) 1275 writer.dump(fd)
1276
1277 - def clean(self):
1278 """ 1279 Clean the cache of components. 1280 """ 1281 self._parser.clean()
1282
1283 - def rebuildNeeded(self):
1284 if not os.path.exists(self.filename): 1285 return True 1286 1287 # A bit complicated because we want to allow FLU_PROJECT_PATH to 1288 # point to nonexistent directories 1289 registryPaths = sets.Set(self._getRegistryPathsFromEnviron()) 1290 oldRegistryPaths = sets.Set([dir.getPath() 1291 for dir in self.getDirectories()]) 1292 if registryPaths != oldRegistryPaths: 1293 if oldRegistryPaths - registryPaths: 1294 return True 1295 if filter(os.path.exists, registryPaths - oldRegistryPaths): 1296 return True 1297 1298 registry_modified = _getMTime(self.filename) 1299 for d in self._parser.getDirectories(): 1300 if d.rebuildNeeded(registry_modified): 1301 return True 1302 1303 return False
1304
1305 - def save(self, force=False):
1306 if not force and not self.rebuildNeeded(): 1307 return 1308 1309 self.info('Saving registry to %s' % self.filename) 1310 1311 # create parent directory 1312 dir = os.path.split(self.filename)[0] 1313 if not os.path.exists(dir): 1314 try: 1315 os.makedirs(dir) 1316 except OSError, e: 1317 if e.errno == errno.EACCES: 1318 self.error('Registry directory %s could not be created !' % 1319 dir) 1320 else: 1321 raise 1322 1323 if not os.path.isdir(dir): 1324 self.error('Registry directory %s is not a directory !') 1325 try: 1326 fd = open(self.filename, 'w') 1327 self.dump(fd) 1328 except IOError, e: 1329 if e.errno == errno.EACCES: 1330 self.error('Registry file %s could not be created !' % 1331 self.filename) 1332 else: 1333 raise
1334
1335 - def _getRegistryPathsFromEnviron(self):
1336 registryPaths = [configure.pythondir, ] 1337 if os.environ.has_key('FLU_PROJECT_PATH'): 1338 paths = os.environ['FLU_PROJECT_PATH'] 1339 registryPaths += paths.split(':') 1340 return registryPaths
1341
1342 - def verify(self, force=False):
1343 """ 1344 Verify if the registry is uptodate and rebuild if it is not. 1345 1346 @param force: True if the registry needs rebuilding for sure. 1347 """ 1348 # construct a list of all paths to scan for registry .xml files 1349 if force or self.rebuildNeeded(): 1350 self.info("Rebuilding registry") 1351 self.clean() 1352 for path in self._getRegistryPathsFromEnviron(): 1353 if not self.addRegistryPath(path): 1354 self._parser.removeDirectoryByPath(path) 1355 self.save(True)
1356
1357 -class RegistrySubsetWriter(RegistryWriter):
1358 - def __init__(self, fromRegistry=None, onlyBundles=None):
1359 """ 1360 @param fromRegistry: The registry to subset, or the default. 1361 @type fromRegistry: L{ComponentRegistry} 1362 @param onlyBundles: If given, only include the subset of the 1363 registry that is provided by bundles whose names are in this 1364 list. 1365 @type onlyBundles: list of str 1366 """ 1367 self.fromRegistry = fromRegistry 1368 self.onlyBundles = onlyBundles
1369
1370 - def dump(self, fd):
1371 reg = self.fromRegistry or getRegistry() 1372 pred = None 1373 if self.onlyBundles is not None: 1374 pred = lambda b: b.name in self.onlyBundles 1375 bundles = filter(pred, reg.getBundles()) 1376 1377 bundledfiles = {} 1378 for b in bundles: 1379 for d in b.getDirectories(): 1380 for f in d.getFiles(): 1381 filename = os.path.join(d.getName(), f.getLocation()) 1382 bundledfiles[filename] = b 1383 1384 def fileIsBundled(basedir, filename): 1385 return os.path.join(basedir, filename) in bundledfiles
1386 1387 pred = lambda c: (filter(lambda f: fileIsBundled(c.getBase(), 1388 f.getFilename()), 1389 c.getFiles()) 1390 or filter(lambda e: fileIsBundled(c.getBase(), 1391 e.getLocation()), 1392 c.getEntries())) 1393 components = filter(pred, reg.getComponents()) 1394 1395 pred = lambda p: p.getEntry().getLocation() in bundledfiles 1396 plugs = filter(pred, reg.getPlugs()) 1397 1398 directories = [] # no need for this 1399 1400 regwriter = RegistryWriter(components, plugs, bundles, directories) 1401 regwriter.dump(fd)
1402 1403 __registry = None 1404
1405 -def getRegistry():
1406 """ 1407 Return the registry. Only one registry will ever be created. 1408 1409 @rtype: L{ComponentRegistry} 1410 """ 1411 global __registry 1412 1413 if not __registry: 1414 log.debug('registry', 'instantiating registry') 1415 __registry = ComponentRegistry() 1416 1417 return __registry
1418