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

Source Code for Module flumotion.common.bundleclient

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_bundleclient -*- 
  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  Bundle fetching, caching, and importing utilities for clients using 
 24  bundled code and data 
 25  """ 
 26   
 27  from twisted.internet import error, defer 
 28   
 29  from flumotion.common import bundle, common, errors, log, package 
 30  from flumotion.configure import configure 
 31  from flumotion.twisted.defer import defer_generator_method 
 32   
 33  __all__ = ['BundleLoader'] 
 34   
35 -class BundleLoader(log.Loggable):
36 """ 37 I am an object that can get and set up bundles from a PB server. 38 39 @cvar remote: a remote reference to an avatar on the PB server. 40 """ 41 remote = None 42 _unbundler = None 43
44 - def __init__(self, remote):
45 """ 46 @type remote: L{twisted.spread.pb.RemoteReference} 47 """ 48 self.remote = remote 49 self._unbundler = bundle.Unbundler(configure.cachedir)
50
51 - def _callRemote(self, methodName, *args, **kwargs):
52 """ 53 Call the given remote method on the manager-side Avatar. 54 """ 55 if not self.remote: 56 raise errors.ManagerNotConnectedError 57 return self.remote.callRemote(methodName, *args, **kwargs)
58
59 - def getBundles(self, **kwargs):
60 # FIXME: later on, split out this method into getBundles which does 61 # not call registerPackagePath, and setupBundles which calls getBundles 62 # and register. Then change getBundles calls to setupBundles. 63 """ 64 Get, extract and register all bundles needed. 65 Either one of bundleName, fileName or moduleName should be specified 66 in **kwargs, which should be strings or lists of strings. 67 68 @returns: a deferred firing a a list of (bundleName, bundlePath) 69 tuples, with lowest dependency first. 70 bundlePath is the directory to register 71 for this package. 72 """ 73 # get sums for all bundles we need 74 d = self._callRemote('getBundleSums', **kwargs) 75 yield d 76 77 # sums is a list of name, sum tuples, highest to lowest 78 # figure out which bundles we're missing 79 try: 80 sums = d.value() 81 except Exception, e: 82 # FIXME: this is stupid 83 raise 84 self.debug('Got sums %r' % sums) 85 toFetch = [] 86 87 import os # fool pychecker 88 89 for name, md5 in sums: 90 path = os.path.join(configure.cachedir, name, md5) 91 if os.path.exists(path): 92 self.log(name + ' is up to date') 93 else: 94 self.log(name + ' needs fetching') 95 toFetch.append(name) 96 97 # ask for the missing bundles 98 d = self._callRemote('getBundleZips', toFetch) 99 yield d 100 101 # unpack the new bundles 102 result = d.value() 103 for name in toFetch: 104 if name not in result.keys(): 105 msg = "Missing bundle %s was not received" % name 106 self.warning(msg) 107 raise errors.NoBundleError(msg) 108 109 b = bundle.Bundle(name) 110 b.setZip(result[name]) 111 path = self._unbundler.unbundle(b) 112 113 # register all package paths; to do so we need to reverse sums 114 sums.reverse() 115 ret = [] 116 for name, md5 in sums: 117 self.log('registerPackagePath for %s' % name) 118 path = os.path.join(configure.cachedir, name, md5) 119 if not os.path.exists(path): 120 self.warning("path %s for bundle %s does not exist", 121 path, name) 122 else: 123 package.getPackager().registerPackagePath(path, name) 124 ret.append((name, path)) 125 126 yield ret
127 getBundles = defer_generator_method(getBundles) 128
129 - def loadModule(self, moduleName):
130 """ 131 Load the module given by name. 132 Sets up all necessary bundles to be able to load the module. 133 134 @rtype: L{twisted.internet.defer.Deferred} 135 @returns: a deferred that will fire when the given module is loaded, 136 giving the loaded module. 137 """ 138 139 # fool pychecker 140 import os # fool pychecker 141 import sys 142 143 self.debug('Loading module %s' % moduleName) 144 145 # get sums for all bundles we need 146 d = self.getBundles(moduleName=moduleName) 147 yield d 148 149 bundles = d.value() 150 self.debug('Got bundles %r' % bundles) 151 152 # load up the module and return it 153 __import__(moduleName, globals(), locals(), []) 154 self.log('loaded module %s' % moduleName) 155 yield sys.modules[moduleName]
156 loadModule = defer_generator_method(loadModule) 157
158 - def getBundleByName(self, bundleName):
159 """ 160 Get the given bundle locally. 161 162 @rtype: L{twisted.internet.defer.Deferred} 163 @returns: a deferred returning the absolute path under which the 164 bundle is extracted. 165 """ 166 self.debug('Getting bundle %s' % bundleName) 167 d = self.getBundles(bundleName=bundleName) 168 yield d 169 170 bundles = d.value() 171 name, path = bundles[-1] 172 assert name == bundleName 173 self.debug('Got bundle %s in %s' % (bundleName, path)) 174 yield path
175 getBundleByName = defer_generator_method(getBundleByName) 176
177 - def getFile(self, fileName):
178 """ 179 Do everything needed to get the given bundled file. 180 181 Returns: a deferred returning the absolute path to a local copy 182 of the given file. 183 """ 184 self.debug('Getting file %s' % fileName) 185 d = self.getBundles(fileName=fileName) 186 yield d 187 188 import os # fool pychecker 189 190 bundles = d.value() 191 name, bundlePath = bundles[-1] 192 path = os.path.join(bundlePath, fileName) 193 if not os.path.exists(path): 194 self.warning("path %s for file %s does not exist" % ( 195 path, fileName)) 196 197 yield path
198 getFile = defer_generator_method(getFile)
199