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  """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  # Re-enable when reading the registry cache is lighter-weight, or we 
  43  # decide that it's a good idea, or something. See #799. 
  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   
58 -def _getMTime(file):
59 return os.stat(file)[stat.ST_MTIME]
60
61 -class RegistryEntryComponent:
62 """ 63 I represent a <component> entry in the registry 64 """ 65 # RegistryEntryComponent has a constructor with a lot of arguments, 66 # but that's ok here. Allow it through pychecker. 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 # we don't want to end up with the string "None" 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 # FIXME: Handle multiple files 141 if len(self.files) > 1: 142 return 143 144 return self.files[0].getFilename()
145
146 - def getType(self):
147 return self.type
148
149 - def getBase(self):
150 return self.base
151
152 - def getDescription(self):
153 return self.description
154
155 - def getSource(self):
156 return self.source
157
158 - def getEaters(self):
159 return self.eaters
160
161 - def getFeeders(self):
162 return self.feeders
163
164 - def getNeedsSynchronization(self):
165 return self.needs_sync
166
167 - def getClockPriority(self):
168 return self.clock_priority
169
170 - def getSockets(self):
171 return self.sockets
172
173 -class RegistryEntryPlug:
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
229 - def getType(self):
230 return self.type
231
232 - def getSocket(self):
233 return self.socket
234
235 -class RegistryEntryBundle:
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
247 - def getName(self):
248 return self.name
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):
263 return self.project
264
265 - def getUnder(self):
266 return self.under
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
275 -class RegistryEntryBundleDirectory:
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
281 - def getName(self):
282 return self.name
283
284 - def getFiles(self):
285 return self.files
286
287 -class RegistryEntryBundleFilename:
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):
294 return self.location
295
296 - def getRelative(self):
297 return self.relative
298
299 -class RegistryEntryProperty:
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 # we don't want to end up with the string "None" 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
314 - def getName(self):
315 return self.name
316
317 - def getType(self):
318 return self.type
319
320 - def getDescription(self):
321 return self.description
322
323 - def isRequired(self):
324 return self.required
325
326 - def isMultiple(self):
327 return self.multiple
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
355 -class RegistryEntryFile:
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
361 - def getName(self):
362 return os.path.basename(self.filename)
363
364 - def getType(self):
365 return self.type
366
367 - def getFilename(self):
368 return self.filename
369
370 - def isType(self, type):
371 return self.type == type
372
373 -class RegistryEntryEntry:
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
380 - def getType(self):
381 return self.type
382
383 - def getLocation(self):
384 return self.location
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):
394 return self.function
395
396 -class RegistryEntryEater:
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
403 - def getName(self):
404 return self.name
405
406 - def getRequired(self):
407 return self.required
408
409 - def getMultiple(self):
410 return self.multiple
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
429 -class RegistryEntryWizardFormat(pb.Copyable):
430 """ 431 This class represents an <accept-format> or <provide-format> 432 entry in the registry 433 """
434 - def __init__(self, media_type):
435 self.media_type = media_type
436 437
438 -class RegistryParser(fxml.Parser):
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
452 - def __init__(self):
453 self.clean()
454
455 - def clean(self):
456 self._components = {} 457 self._directories = {} # path -> RegistryDirectory 458 self._bundles = {} 459 self._plugs = {}
460
461 - def getComponents(self):
462 return self._components.values()
463
464 - def getComponent(self, name):
465 try: 466 return self._components[name] 467 except KeyError: 468 raise errors.UnknownComponentError("unknown component type:" 469 " %s" % (name,))
470
471 - def getPlugs(self):
472 return self._plugs.values()
473
474 - def getPlug(self, name):
475 try: 476 return self._plugs[name] 477 except KeyError: 478 raise errors.UnknownPlugError("unknown plug type: %s" 479 % (name,))
480
481 - def _parseComponents(self, node):
482 # <components> 483 # <component> 484 # </components> 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
495 - def _parseComponent(self, node):
496 # <component type="..." base="..." description="..."> 497 # <source> 498 # <eater> 499 # <feeder> 500 # <properties> 501 # <entries> 502 # <synchronization> 503 # <sockets> 504 # <wizard> 505 # </component> 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 # Merge in options for inherit 521 #if node.hasAttribute('inherit'): 522 # base_type = str(node.getAttribute('inherit')) 523 # base = self.getComponent(base_type) 524 # for prop in base.getProperties(): 525 # properties[prop.getName()] = prop 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
551 - def _parseSource(self, node):
552 # <source location="..."/> 553 location, = self.parseAttributes(node, ('location',)) 554 return location
555
556 - def _parseProperty(self, node):
557 # <property name="..." type="" required="yes/no" multiple="yes/no"/> 558 # returns: RegistryEntryProperty 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
568 - def _parseCompoundProperty(self, node):
569 # <compound-property name="..." required="yes/no" multiple="yes/no"> 570 # <property ... />* 571 # <compound-property ... >...</compound-property>* 572 # </compound-property> 573 # returns: RegistryEntryCompoundProperty 574 575 attrs = self.parseAttributes(node, required=('name',), 576 optional=('required', 'multiple', 'description')) 577 name, required, multiple, description = attrs 578 required = common.strToBool(required) 579 multiple = common.strToBool(multiple) 580 581 properties = {} 582 def addProperty(prop): 583 properties[prop.getName()] = prop
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
593 - def _parseProperties(self, node):
594 # <properties> 595 # <property>* 596 # <compound-property>* 597 # </properties> 598 599 properties = {} 600 def addProperty(prop): 601 properties[prop.getName()] = prop
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
611 - def _parseFile(self, node):
612 # <file name="..." type=""/> 613 # returns: RegistryEntryFile 614 615 name, fileType = self.parseAttributes(node, ('name', 'type')) 616 directory = os.path.split(self.filename)[0] 617 filename = os.path.join(directory, name) 618 return RegistryEntryFile(filename, fileType)
619
620 - def _parseFiles(self, node):
621 # <files> 622 # <file> 623 # </files> 624 625 files = [] 626 parsers = {'file': (self._parseFile, files.append)} 627 628 self.parseFromTable(node, parsers) 629 630 return files
631
632 - def _parseSocket(self, node):
633 # <socket type=""/> 634 # returns: str of the type 635 636 socketType, = self.parseAttributes(node, ('type',)) 637 return socketType
638
639 - def _parseSockets(self, node):
640 # <sockets> 641 # <socket> 642 # </sockets> 643 644 sockets = [] 645 parsers = {'socket': (self._parseSocket, sockets.append)} 646 647 self.parseFromTable(node, parsers) 648 649 return sockets
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
656 - def _parseEntries(self, node):
657 # <entries> 658 # <entry> 659 # </entries> 660 # returns: dict of type -> entry 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
675 - def _parseEater(self, node):
676 # <eater name="..." [required="yes/no"] [multiple="yes/no"]/> 677 attrs = self.parseAttributes(node, ('name',), ('required', 'multiple')) 678 name, required, multiple = attrs 679 # only required defaults to True 680 required = common.strToBool(required or 'True') 681 multiple = common.strToBool(multiple) 682 683 return RegistryEntryEater(name, required, multiple)
684
685 - def _parseFeeder(self, node):
686 # <feeder name="..."/> 687 name, = self.parseAttributes(node, ('name',)) 688 return name
689
690 - def _parseSynchronization(self, node):
691 # <synchronization [required="yes/no"] [clock-priority="100"]/> 692 attrs = self.parseAttributes(node, (), ('required', 'clock-priority')) 693 required, clock_priority = attrs 694 required = common.strToBool(required) 695 clock_priority = int(clock_priority or '100') 696 return required, clock_priority
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
705 - def _parseDefaultPlugEntry(self, node):
706 return {'default': self._parsePlugEntry(node)}
707
708 - def _parsePlugEntries(self, node):
709 # <entries> 710 # <entry> 711 # </entries> 712 # returns: dict of type -> entry 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
727 - def _parsePlug(self, node):
728 # <plug socket="..." type="..."> 729 # <entries> 730 # <entry> 731 # <properties> 732 # <wizard> 733 # </plug> 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 # backwards compatibility 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
759 - def _parsePlugs(self, node):
760 # <plugs> 761 # <plug> 762 # </plugs> 763 764 self.checkAttributes(node) 765 766 plugs = {} 767 def addPlug(plug): 768 plugs[plug.getType()] = plug
769 770 parsers = {'plug': (self._parsePlug, addPlug)} 771 self.parseFromTable(node, parsers) 772 773 return plugs 774 775 ## Component registry specific functions
776 - def parseRegistryFile(self, file):
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 # ignore silently, since this function is used to parse all 791 # .xml files encountered 792 self.debug('%s does not have registry as root tag' % self.filename) 793 return 794 795 # shouldn't have <directories> elements in registry fragments 796 self._parseRoot(node, disallowed=['directories']) 797 root.unlink()
798
799 - def _parseBundles(self, node):
800 # <bundles> 801 # <bundle> 802 # </bundles> 803 804 bundles = {} 805 def addBundle(bundle): 806 bundles[bundle.getName()] = bundle
807 808 parsers = {'bundle': (self._parseBundle, addBundle)} 809 self.parseFromTable(node, parsers) 810 811 return bundles 812
813 - def _parseBundle(self, node):
814 # <bundle name="..."> 815 # <dependencies> 816 # <directories> 817 # </bundle> 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
835 - def _parseBundleDependency(self, node):
836 name, = self.parseAttributes(node, ('name',)) 837 return name
838
839 - def _parseBundleDependencies(self, node):
840 # <dependencies> 841 # <dependency name=""> 842 # </dependencies> 843 dependencies = [] 844 845 parsers = {'dependency': (self._parseBundleDependency, 846 dependencies.append)} 847 self.parseFromTable(node, parsers) 848 849 return dependencies
850
851 - def _parseBundleDirectories(self, node):
852 # <directories> 853 # <directory> 854 # </directories> 855 directories = [] 856 857 parsers = {'directory': (self._parseBundleDirectory, 858 directories.append)} 859 self.parseFromTable(node, parsers) 860 861 return directories
862
863 - def _parseBundleDirectoryFilename(self, node, name):
864 attrs = self.parseAttributes(node, ('location',), ('relative',)) 865 location, relative = attrs 866 867 if not relative: 868 relative = os.path.join(name, location) 869 870 return RegistryEntryBundleFilename(location, relative)
871
872 - def _parseBundleDirectory(self, node):
873 # <directory name=""> 874 # <filename location="" [ relative="" ] > 875 # </directory> 876 name, = self.parseAttributes(node, ('name',)) 877 878 filenames = [] 879 def parseFilename(node): 880 return self._parseBundleDirectoryFilename(node, name)
881 882 parsers = {'filename': (parseFilename, filenames.append)} 883 self.parseFromTable(node, parsers) 884 885 return RegistryEntryBundleDirectory(name, filenames) 886 887 ## Base registry specific functions
888 - def parseRegistry(self, file):
889 """ 890 @param file: The file to parse, either as an open file object, 891 or as the name of a file to open. 892 @type file: str or file. 893 """ 894 if isinstance(file, basestring): 895 self.filename = file 896 else: 897 self.filename = getattr(file, 'name', '<string>') 898 root = self.getRoot(file) 899 self._parseRoot(root.documentElement) 900 root.unlink()
901
902 - def getDirectories(self):
903 return self._directories.values()
904
905 - def getDirectory(self, name):
906 return self._directories[name]
907
908 - def addDirectory(self, directory):
909 """ 910 Add a registry path object to the parser. 911 912 @type directory: {RegistryDirectory} 913 """ 914 self._directories[directory.getPath()] = directory
915
916 - def removeDirectoryByPath(self, path):
917 """ 918 Remove a directory from the parser given the path. 919 Used when the path does not actually contain any registry information. 920 """ 921 if path in self._directories.keys(): 922 del self._directories[path]
923
924 - def _parseRoot(self, node, disallowed=None):
925 # <components>...</components>* 926 # <plugs>...</plugs>* 927 # <directories>...</directories>* 928 # <bundles>...</bundles>* 929 parsers = {'components': (self._parseComponents, 930 self._components.update), 931 'directories': (self._parseDirectories, 932 self._directories.update), 933 'bundles': (self._parseBundles, self._bundles.update), 934 'plugs': (self._parsePlugs, self._plugs.update)} 935 936 if disallowed: 937 for k in disallowed: 938 del parsers[k] 939 940 self.parseFromTable(node, parsers)
941
942 - def _parseDirectories(self, node):
943 # <directories> 944 # <directory> 945 # </directories> 946 947 directories = {} 948 def addDirectory(d): 949 directories[d.getPath()] = d
950 951 parsers = {'directory': (self._parseDirectory, addDirectory)} 952 self.parseFromTable(node, parsers) 953 954 return directories 955
956 - def _parseDirectory(self, node):
957 # <directory filename="..."/> 958 filename, = self.parseAttributes(node, ('filename',)) 959 return RegistryDirectory(filename)
960
961 - def _parseComponentWizard(self, node):
962 return self._parseWizard(node, _VALID_WIZARD_COMPONENT_TYPES)
963
964 - def _parsePlugWizard(self, node):
965 return self._parseWizard(node, _VALID_WIZARD_PLUG_TYPES)
966
967 - def _parseWizard(self, node, validTypes):
968 # <wizard type="..." _description=" " feeder="..." eater="..."]/> 969 # 970 # NOTE: We are using _description with the leading underscore for 971 # the case of intltool, it is not possible for it to pickup 972 # translated attributes otherwise. Ideally we would use another 973 # tool so we can avoid underscores in our xml schema. 974 attrs = self.parseAttributes(node, 975 ('type', '_description'), 976 ('feeder', 'eater')) 977 wizardType, description, feeder, eater = attrs 978 979 accepts = [] 980 provides = [] 981 parsers = { 982 'accept-format': (self._parseAcceptFormat, 983 lambda n: accepts.append(n)), 984 'provide-format': (self._parseProvideFormat, 985 lambda n: provides.append(n)), 986 } 987 self.parseFromTable(node, parsers) 988 989 parent_type = node.parentNode.getAttribute('type') 990 991 if not wizardType in validTypes: 992 raise fxml.ParserError( 993 "<wizard>'s type attribute is %s must be one of %s" % ( 994 parent_type, 995 ', '.join(validTypes))) 996 997 isProducer = wizardType.endswith('-producer') 998 isEncoder = wizardType.endswith('-encoder') 999 isMuxer = (wizardType == 'muxer') 1000 isConsumer = wizardType.endswith('-consumer') 1001 1002 err = None 1003 # Producers and Encoders cannot have provided 1004 if accepts and (isProducer or isEncoder): 1005 err = ('<wizard type="%s"> does not allow an accepted ' 1006 'media-type.') % (parent_type,) 1007 # Encoders, Muxers and Consumers must have an accepted 1008 elif not accepts and (isMuxer or isConsumer): 1009 err = ('<wizard type="%s"> requires at least one accepted ' 1010 'media-type.') % (parent_type,) 1011 # Producers and Consumers cannot have provided 1012 elif provides and (isProducer or isConsumer): 1013 err = ('<wizard type="%s"> does not allow a provided ' 1014 'media-type.') % (parent_type,) 1015 # Producers, Encoders and Muxers must have exactly one provided 1016 if len(provides) != 1 and (isEncoder or isMuxer): 1017 err = ('<wizard type="%s"> requires exactly one provided ' 1018 'media-type.') % (parent_type,) 1019 1020 if err: 1021 raise fxml.ParserError(err) 1022 1023 return RegistryEntryWizard(parent_type, wizardType, description, 1024 feeder, eater, accepts, provides)
1025
1026 - def _parseAcceptFormat(self, node):
1027 # <accept-format media-type="..."/> 1028 media_type, = self.parseAttributes(node, ('media-type',)) 1029 return RegistryEntryWizardFormat(media_type)
1030
1031 - def _parseProvideFormat(self, node):
1032 # <provide-format media-type="..."/> 1033 media_type, = self.parseAttributes(node, ('media-type',)) 1034 return RegistryEntryWizardFormat(media_type)
1035 1036 1037 # FIXME: filename -> path
1038 -class RegistryDirectory(log.Loggable):
1039 """ 1040 I represent a directory under a path managed by the registry. 1041 I can be queried for a list of partial registry .xml files underneath 1042 the given path, under the given prefix. 1043 """
1044 - def __init__(self, path, prefix=configure.PACKAGE):
1045 self._path = path 1046 self._prefix = prefix 1047 scanPath = os.path.join(path, prefix) 1048 self._files, self._dirs = self._getFileLists(scanPath)
1049
1050 - def __repr__(self):
1051 return "<RegistryDirectory %s>" % self._path
1052
1053 - def _getFileLists(self, root):
1054 """ 1055 Get all files ending in .xml from all directories under the given root. 1056 1057 @type root: string 1058 @param root: the root directory under which to search 1059 1060 @returns: a list of .xml files, relative to the given root directory 1061 """ 1062 files = [] 1063 dirs = [] 1064 1065 if os.path.exists(root): 1066 try: 1067 directory_files = os.listdir(root) 1068 except OSError, e: 1069 if e.errno == errno.EACCES: 1070 return files, dirs 1071 else: 1072 raise 1073 1074 dirs.append(root) 1075 1076 for entry in directory_files: 1077 path = os.path.join(root, entry) 1078 # if it's a .xml file, then add it to the list 1079 if not os.path.isdir(path): 1080 if path.endswith('.xml'): 1081 files.append(path) 1082 # if it's a directory and not an svn directory, then get 1083 # its files and add them 1084 elif entry != '.svn': 1085 newFiles, newDirs = self._getFileLists(path) 1086 files.extend(newFiles) 1087 dirs.extend(newDirs) 1088 1089 return files, dirs
1090
1091 - def rebuildNeeded(self, mtime):
1092 def _rebuildNeeded(file): 1093 try: 1094 if _getMTime(file) > mtime: 1095 self.debug("Path %s changed since registry last " 1096 "scanned", f) 1097 return True 1098 return False 1099 except OSError: 1100 self.debug("Failed to stat file %s, need to rescan", f) 1101 return True
1102 1103 for f in self._files: 1104 if _rebuildNeeded(f): 1105 return True 1106 for f in self._dirs: 1107 if _rebuildNeeded(f): 1108 return True 1109 return False
1110
1111 - def getFiles(self):
1112 """ 1113 Return a list of all .xml registry files underneath this registry 1114 path. 1115 """ 1116 return self._files
1117
1118 - def getPath(self):
1119 return self._path
1120
1121 -class RegistryWriter(log.Loggable):
1122 - def __init__(self, components, plugs, bundles, directories):
1123 """ 1124 @param components: components to write 1125 @type components: list of L{RegistryEntryComponent} 1126 @param plugs: plugs to write 1127 @type plugs: list of L{RegistryEntryPlug} 1128 @param bundles: bundles to write 1129 @type bundles: list of L{RegistryEntryBundle} 1130 @param directories: directories to write 1131 @type directories: list of L{RegistryEntryBundleDirectory} 1132 """ 1133 self.components = components 1134 self.plugs = plugs 1135 self.bundles = bundles 1136 self.directories = directories
1137
1138 - def dump(self, fd):
1139 """ 1140 Dump the cache of components to the given opened file descriptor. 1141 1142 @type fd: integer 1143 @param fd: open file descriptor to write to 1144 """ 1145 1146 def w(i, msg): 1147 print >> fd, ' '*i + msg
1148 def e(attr): 1149 return saxutils.quoteattr(attr)
1150 1151 def _dump_proplist(i, proplist, ioff=2): 1152 for prop in proplist: 1153 if isinstance(prop, RegistryEntryCompoundProperty): 1154 _dump_compound(i, prop) 1155 else: 1156 w(i, ('<property name="%s" type="%s"' 1157 % (prop.getName(), prop.getType()))) 1158 w(i, (' description=%s' 1159 % (e(prop.getDescription()),))) 1160 w(i, (' required="%s" multiple="%s"/>' 1161 % (prop.isRequired(), prop.isMultiple()))) 1162 1163 def _dump_compound(i, cprop, ioff=2): 1164 w(i, ('<compound-property name="%s"' % (cprop.getName(),))) 1165 w(i, (' description=%s' 1166 % (e(cprop.getDescription()),))) 1167 w(i, (' required="%s" multiple="%s">' 1168 % (cprop.isRequired(), cprop.isMultiple()))) 1169 _dump_proplist(i + ioff, cprop.getProperties()) 1170 w(i, ('</compound-property>')) 1171 1172 def _dump_entries(i, entries): 1173 if not entries: 1174 return 1175 1176 w(i, '<entries>') 1177 for entry in entries: 1178 w(i+2, '<entry type="%s" location="%s" function="%s"/>' % ( 1179 entry.getType(), 1180 entry.getLocation(), 1181 entry.getFunction())) 1182 w(i, '</entries>') 1183 1184 w(0, '<registry>') 1185 w(0, '') 1186 1187 # Write components 1188 w(2, '<components>') 1189 w(0, '') 1190 for component in self.components: 1191 w(4, '<component type="%s" base="%s"' % ( 1192 component.getType(), component.getBase())) 1193 w(4, ' description=%s>' 1194 % (e(component.getDescription()),)) 1195 1196 w(6, '<source location="%s"/>' % component.getSource()) 1197 for x in component.getEaters(): 1198 w(6, '<eater name="%s" required="%s" multiple="%s"/>' 1199 % (x.getName(), x.getRequired() and "yes" or "no", 1200 x.getMultiple() and "yes" or "no")) 1201 for x in component.getFeeders(): 1202 w(6, '<feeder name="%s"/>' % x) 1203 w(6, '<synchronization required="%s" clock-priority="%d"/>' 1204 % (component.getNeedsSynchronization() and "yes" or "no", 1205 component.getClockPriority())) 1206 1207 sockets = component.getSockets() 1208 if sockets: 1209 w(6, '<sockets>') 1210 for socket in sockets: 1211 w(8, '<socket type="%s"/>' % socket) 1212 w(6, '</sockets>') 1213 1214 w(6, '<properties>') 1215 _dump_proplist(8, component.getProperties()) 1216 w(6, '</properties>') 1217 1218 for wizard in component.wizards: 1219 w(6, '<wizard type="%s" _description="%s" feeder="%s">' % ( 1220 wizard.type, 1221 e(wizard.description), 1222 wizard.feeder)) 1223 for accept in wizard.accepts: 1224 w(8, '<accept-format media-type="%s"/>' % ( 1225 accept.media_type)) 1226 for provide in wizard.provides: 1227 w(8, '<provide-format media-type="%s"/>' % ( 1228 provide.media_type)) 1229 w(6, '</wizard>') 1230 1231 registryEntryFiles = component.getFiles() 1232 if registryEntryFiles: 1233 w(6, '<files>') 1234 for entryFile in registryEntryFiles: 1235 w(8, '<file name="%s" type="%s"/>' % ( 1236 entryFile.getName(), 1237 entryFile.getType())) 1238 w(6, '</files>') 1239 1240 _dump_entries(6, component.getEntries()) 1241 1242 w(4, '</component>') 1243 w(0, '') 1244 1245 w(2, '</components>') 1246 w(0, '') 1247 1248 # Write plugs 1249 w(2, '<plugs>') 1250 w(0, '') 1251 for plug in self.plugs: 1252 w(4, '<plug type="%s" socket="%s">' 1253 % (plug.getType(), plug.getSocket())) 1254 1255 _dump_entries(6, plug.getEntries()) 1256 1257 w(6, '<properties>') 1258 _dump_proplist(8, plug.getProperties()) 1259 w(6, '</properties>') 1260 1261 w(4, '</plug>') 1262 w(0, '') 1263 1264 w(2, '</plugs>') 1265 w(0, '') 1266 1267 # bundles 1268 w(2, '<bundles>') 1269 for bundle in self.bundles: 1270 w(4, '<bundle name="%s" under="%s" project="%s">' % ( 1271 bundle.getName(), bundle.getUnder(), bundle.getProject())) 1272 1273 dependencies = bundle.getDependencies() 1274 if dependencies: 1275 w(6, '<dependencies>') 1276 for dependency in dependencies: 1277 w(8, '<dependency name="%s"/>' % dependency) 1278 w(6, '</dependencies>') 1279 1280 bundleDirectories = bundle.getDirectories() 1281 if bundleDirectories: 1282 w(6, '<directories>') 1283 for directory in bundleDirectories: 1284 w(8, '<directory name="%s">' % directory.getName()) 1285 for filename in directory.getFiles(): 1286 w(10, '<filename location="%s" relative="%s"/>' % ( 1287 filename.getLocation(), filename.getRelative())) 1288 w(8, '</directory>') 1289 w(6, '</directories>') 1290 1291 w(4, '</bundle>') 1292 w(0, '') 1293 w(2, '</bundles>') 1294 1295 1296 # Directories 1297 directories = self.directories 1298 if directories: 1299 w(2, '<directories>') 1300 w(0, '') 1301 for d in directories: 1302 w(4, '<directory filename="%s"/>' % d.getPath()) 1303 w(2, '</directories>') 1304 w(0, '') 1305 1306 w(0, '</registry>') 1307
1308 -class ComponentRegistry(log.Loggable):
1309 """Registry, this is normally not instantiated.""" 1310 1311 logCategory = 'registry' 1312 filename = os.path.join(configure.registrydir, 'registry.xml') 1313
1314 - def __init__(self):
1315 self._parser = RegistryParser() 1316 1317 if (READ_CACHE and 1318 os.path.exists(self.filename) and 1319 os.access(self.filename, os.R_OK)): 1320 self.info('Parsing registry: %s' % self.filename) 1321 try: 1322 self._parser.parseRegistry(self.filename) 1323 except fxml.ParserError, e: 1324 # this can happen for example if we upgraded to a new version, 1325 # ran, then downgraded again; the registry can then contain 1326 # XML keys that are not understood by this version. 1327 # This is non-fatal, and gets fixed due to a re-scan 1328 self.warning('Could not parse registry %s.' % self.filename) 1329 self.debug('fxml.ParserError: %s' % log.getExceptionMessage(e)) 1330 1331 self.verify(force=not READ_CACHE)
1332
1333 - def addFile(self, file):
1334 """ 1335 @param file: The file to add, either as an open file object, or 1336 as the name of a file to open. 1337 @type file: str or file. 1338 """ 1339 if isinstance(file, str) and file.endswith('registry.xml'): 1340 self.warning('%s seems to be an old registry in your tree, ' 1341 'please remove it', file) 1342 self.debug('Adding file: %r', file) 1343 self._parser.parseRegistryFile(file)
1344
1345 - def addFromString(self, string):
1346 f = StringIO(string) 1347 self.addFile(f) 1348 f.close()
1349
1350 - def addRegistryPath(self, path, prefix=configure.PACKAGE):
1351 """ 1352 Add a registry path to this registry, scanning it for registry 1353 snippets. 1354 1355 @param path: a full path containing a 'flumotion' directory, 1356 which will be scanned for registry files. 1357 1358 @rtype: bool 1359 @returns: whether the path could be added 1360 """ 1361 self.debug('path %s, prefix %s' % (path, prefix)) 1362 if not os.path.exists(path): 1363 self.warning("Cannot add non-existent path '%s' to registry" % path) 1364 return False 1365 if not os.path.exists(os.path.join(path, prefix)): 1366 self.warning("Cannot add path '%s' to registry " 1367 "since it does not contain prefix '%s'" % (path, prefix)) 1368 return False 1369 1370 # registry path was either not watched or updated, or a force was 1371 # asked, so reparse 1372 self.info('Scanning registry path %s' % path) 1373 registryPath = RegistryDirectory(path, prefix=prefix) 1374 files = registryPath.getFiles() 1375 self.debug('Found %d possible registry files' % len(files)) 1376 map(self.addFile, files) 1377 1378 self._parser.addDirectory(registryPath) 1379 return True
1380 1381 # fixme: these methods inconsistenly molest and duplicate those of 1382 # the parser.
1383 - def isEmpty(self):
1384 return len(self._parser._components) == 0
1385
1386 - def getComponent(self, name):
1387 """ 1388 @rtype: L{RegistryEntryComponent} 1389 """ 1390 return self._parser.getComponent(name)
1391
1392 - def hasComponent(self, name):
1393 return name in self._parser._components
1394
1395 - def getComponents(self):
1396 return self._parser.getComponents()
1397
1398 - def getPlug(self, type):
1399 """ 1400 @rtype: L{RegistryEntryPlug} 1401 """ 1402 return self._parser.getPlug(type)
1403
1404 - def hasPlug(self, name):
1405 return name in self._parser._plugs
1406
1407 - def getPlugs(self):
1408 return self._parser.getPlugs()
1409
1410 - def getBundles(self):
1411 return self._parser._bundles.values()
1412
1413 - def getDirectories(self):
1414 return self._parser.getDirectories()
1415
1416 - def makeBundlerBasket(self):
1417 """ 1418 @rtype: L{flumotion.common.bundle.BundlerBasket} 1419 """ 1420 def load(): 1421 ret = BundlerBasket() 1422 for b in self.getBundles(): 1423 bundleName = b.getName() 1424 self.debug('Adding bundle %s' % bundleName) 1425 for d in b.getDirectories(): 1426 directory = d.getName() 1427 for bundleFilename in d.getFiles(): 1428 try: 1429 basedir = b.getBaseDir() 1430 except errors.NoProjectError, e: 1431 self.warning("Could not load project %s" % e.args) 1432 raise 1433 fullpath = os.path.join(basedir, directory, 1434 bundleFilename.getLocation()) 1435 relative = bundleFilename.getRelative() 1436 self.log('Adding path %s as %s to bundle %s' % ( 1437 fullpath, relative, bundleName)) 1438 try: 1439 ret.add(bundleName, fullpath, relative) 1440 except Exception, e: 1441 self.debug("Reason: %r" % e) 1442 raise RuntimeError( 1443 'Could not add %s to bundle %s (%s)' 1444 % (fullpath, bundleName, e)) 1445 for d in b.getDependencies(): 1446 self.log('Adding dependency of %s on %s' % (bundleName, d)) 1447 ret.depend(bundleName, d) 1448 return ret
1449 1450 try: 1451 return load() 1452 except Exception, e: 1453 self.debug("Could not register bundles the first time: %s" % 1454 log.getExceptionMessage(e)) 1455 self.warning("Bundle problem, rebuilding registry") 1456 self.verify(force=True) 1457 try: 1458 return load() 1459 except Exception, e: 1460 self.debug("Could not register bundles the second time: %s" % 1461 log.getExceptionMessage(e)) 1462 self.error("Could not not register bundles (%s)" % 1463 log.getExceptionMessage(e))
1464
1465 - def dump(self, fd):
1466 """ 1467 Dump the cache of components to the given opened file descriptor. 1468 1469 @type fd: integer 1470 @param fd: open file descriptor to write to 1471 """ 1472 writer = RegistryWriter(self.getComponents(), self.getPlugs(), 1473 self.getBundles(), self.getDirectories()) 1474 writer.dump(fd)
1475
1476 - def clean(self):
1477 """ 1478 Clean the cache of components. 1479 """ 1480 self._parser.clean()
1481
1482 - def rebuildNeeded(self):
1483 if not os.path.exists(self.filename): 1484 return True 1485 1486 # A bit complicated because we want to allow FLU_PROJECT_PATH to 1487 # point to nonexistent directories 1488 registryPaths = sets.Set(self._getRegistryPathsFromEnviron()) 1489 oldRegistryPaths = sets.Set([directory.getPath() 1490 for directory in self.getDirectories()]) 1491 if registryPaths != oldRegistryPaths: 1492 if oldRegistryPaths - registryPaths: 1493 return True 1494 if filter(os.path.exists, registryPaths - oldRegistryPaths): 1495 return True 1496 1497 registry_modified = _getMTime(self.filename) 1498 for d in self._parser.getDirectories(): 1499 if d.rebuildNeeded(registry_modified): 1500 return True 1501 1502 return False
1503
1504 - def save(self, force=False):
1505 if not force and not self.rebuildNeeded(): 1506 return 1507 1508 self.info('Saving registry to %s' % self.filename) 1509 1510 # create parent directory 1511 directory = os.path.split(self.filename)[0] 1512 if not os.path.exists(directory): 1513 try: 1514 makedirs(directory) 1515 except OSError, e: 1516 if e.errno == errno.EACCES: 1517 self.error('Registry directory %s could not be created !' % 1518 directory) 1519 else: 1520 raise 1521 1522 if not os.path.isdir(directory): 1523 self.error('Registry directory %s is not a directory !') 1524 try: 1525 fd = open(self.filename, 'w') 1526 self.dump(fd) 1527 except IOError, e: 1528 if e.errno == errno.EACCES: 1529 self.error('Registry file %s could not be created !' % 1530 self.filename) 1531 else: 1532 raise
1533
1534 - def _getRegistryPathsFromEnviron(self):
1535 registryPaths = [configure.pythondir, ] 1536 if os.environ.has_key('FLU_PROJECT_PATH'): 1537 paths = os.environ['FLU_PROJECT_PATH'] 1538 registryPaths += paths.split(':') 1539 return registryPaths
1540
1541 - def verify(self, force=False):
1542 """ 1543 Verify if the registry is uptodate and rebuild if it is not. 1544 1545 @param force: True if the registry needs rebuilding for sure. 1546 """ 1547 # construct a list of all paths to scan for registry .xml files 1548 if force or self.rebuildNeeded(): 1549 self.info("Rebuilding registry") 1550 if force: 1551 self.info("Rebuild of registry is forced") 1552 if self.rebuildNeeded(): 1553 self.info("Rebuild of registry is needed") 1554 self.clean() 1555 for path in self._getRegistryPathsFromEnviron(): 1556 if not self.addRegistryPath(path): 1557 self._parser.removeDirectoryByPath(path) 1558 self.save(True)
1559
1560 -class RegistrySubsetWriter(RegistryWriter):
1561 - def __init__(self, fromRegistry=None, onlyBundles=None):
1562 """ 1563 @param fromRegistry: The registry to subset, or the default. 1564 @type fromRegistry: L{ComponentRegistry} 1565 @param onlyBundles: If given, only include the subset of the 1566 registry that is provided by bundles whose names are in this 1567 list. 1568 @type onlyBundles: list of str 1569 """ 1570 self.fromRegistry = fromRegistry 1571 self.onlyBundles = onlyBundles
1572
1573 - def dump(self, fd):
1574 reg = self.fromRegistry or getRegistry() 1575 pred = None 1576 bundles = reg.getBundles() 1577 if self.onlyBundles is not None: 1578 bundles = [b for b in bundles 1579 if b.name in self.onlyBundles] 1580 1581 bundledfiles = {} 1582 for b in bundles: 1583 for d in b.getDirectories(): 1584 for f in d.getFiles(): 1585 filename = os.path.join(d.getName(), f.getLocation()) 1586 bundledfiles[filename] = b 1587 1588 def fileIsBundled(basedir, filename): 1589 return os.path.join(basedir, filename) in bundledfiles
1590 1591 pred = lambda c: (filter(lambda f: fileIsBundled(c.getBase(), 1592 f.getFilename()), 1593 c.getFiles()) 1594 or filter(lambda e: fileIsBundled(c.getBase(), 1595 e.getLocation()), 1596 c.getEntries())) 1597 components = filter(pred, reg.getComponents()) 1598 1599 pred = lambda p: p.getEntry().getLocation() in bundledfiles 1600 plugs = filter(pred, reg.getPlugs()) 1601 1602 directories = [] # no need for this 1603 1604 regwriter = RegistryWriter(components, plugs, bundles, directories) 1605 regwriter.dump(fd)
1606 1607 __registry = None 1608 1609
1610 -def makeBundleFromLoadedModules(outfile, outreg, *prefixes):
1611 """ 1612 Make a bundle from a subset of all loaded modules, also writing out 1613 a registry file that can apply to that subset of the global 1614 registry. Suitable for use as a FLU_ATEXIT handler. 1615 1616 @param outfile: The path to which a zip file will be written. 1617 @type outfile: str 1618 @param outreg: The path to which a registry file will be written. 1619 @type outreg: str 1620 @param prefixes: A list of prefixes to which to limit the export. If 1621 not given, package up all modules. For example, "flumotion" would 1622 limit the output to modules that start with "flumotion". 1623 @type prefixes: list of str 1624 """ 1625 from twisted.python import reflect 1626 1627 def getUsedModules(prefixes): 1628 ret = {} 1629 for modname in sys.modules: 1630 if prefixes and not filter(modname.startswith, prefixes): 1631 continue 1632 try: 1633 module = reflect.namedModule(modname) 1634 if hasattr(module, '__file__'): 1635 ret[modname] = module 1636 else: 1637 log.info('makebundle', 'Module %s has no file', module) 1638 except ImportError: 1639 log.info('makebundle', 'Could not import %s', modname) 1640 return ret
1641 1642 def calculateModuleBundleMap(): 1643 allbundles = getRegistry().getBundles() 1644 ret = {} 1645 for bundle in allbundles: 1646 for directory in bundle.getDirectories(): 1647 for bundleFile in directory.getFiles(): 1648 path = os.path.join(directory.getName(), 1649 bundleFile.getLocation()) 1650 parts = path.split(os.path.sep) 1651 if parts[-1].startswith('__init__.py'): 1652 parts.pop() 1653 elif parts[-1].endswith('.py'): 1654 parts[-1] = parts[-1][:-3] 1655 else: 1656 # not a bundled module 1657 continue 1658 modname = '.'.join(parts) 1659 ret[modname] = bundle 1660 return ret 1661 1662 def makeMergedBundler(modules, modulebundlemap): 1663 ret = MergedBundler() 1664 basket = getRegistry().makeBundlerBasket() 1665 for modname in modules: 1666 modfilename = modules[modname].__file__ 1667 if modname in modulebundlemap: 1668 bundleName = modulebundlemap[modname].getName() 1669 for depBundleName in basket.getDependencies(bundleName): 1670 ret.addBundler(basket.getBundlerByName(depBundleName)) 1671 else: 1672 if modfilename.endswith('.pyc'): 1673 modfilename = modfilename[:-1] 1674 if os.path.isdir(modfilename): 1675 with_init = os.path.join(modfilename, '__init__.py') 1676 if os.path.exists(with_init): 1677 modfilename = with_init 1678 nparts = len(modname.split('.')) 1679 if '__init__' in modfilename: 1680 nparts += 1 1681 relpath = os.path.join(*modfilename.split(os.path.sep)[-nparts:]) 1682 ret.add(modfilename, relpath) 1683 return ret 1684 1685 modules = getUsedModules(prefixes) 1686 modulebundlemap = calculateModuleBundleMap() 1687 bundler = makeMergedBundler(modules, modulebundlemap) 1688 1689 print 'Writing bundle to', outfile 1690 open(outfile, 'w').write(bundler.bundle().getZip()) 1691 1692 print 'Writing registry to', outreg 1693 bundlers_used = [b.name for b in bundler.getSubBundlers()] 1694 regwriter = RegistrySubsetWriter(onlyBundles=bundlers_used) 1695 regwriter.dump(open(outreg, 'w')) 1696
1697 -def getRegistry():
1698 """ 1699 Return the registry. Only one registry will ever be created. 1700 1701 @rtype: L{ComponentRegistry} 1702 """ 1703 global __registry 1704 1705 if not __registry: 1706 log.debug('registry', 'instantiating registry') 1707 __registry = ComponentRegistry() 1708 1709 return __registry
1710