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