1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """bundle interface for fetching, caching and importing
23 """
24
25 import os
26 import sys
27
28 from twisted.internet import error, defer
29
30 from flumotion.common import bundle, common, errors, log, package
31 from flumotion.configure import configure
32
33 __all__ = ['BundleLoader']
34 __version__ = "$Rev$"
35
36
38 """
39 I am an object that can get and set up bundles from a PB server.
40
41 @cvar remote: a remote reference to an avatar on the PB server.
42 """
43 remote = None
44 _unbundler = None
45
52
54
55
56
57 """
58 Get, extract and register all bundles needed.
59 Either one of bundleName, fileName or moduleName should be specified
60 in **kwargs, which should be strings or lists of strings.
61
62 @returns: a deferred firing a a list of (bundleName, bundlePath)
63 tuples, with lowest dependency first.
64 bundlePath is the directory to register
65 for this package.
66 """
67
68 def annotated(d, *extraVals):
69
70 def annotatedReturn(ret):
71 return (ret, ) + extraVals
72 d.addCallback(annotatedReturn)
73 return d
74
75 def getZips(sums):
76
77
78 toFetch = []
79 for name, md5 in sums:
80 path = os.path.join(configure.cachedir, name, md5)
81 if os.path.exists(path):
82 self.log('%s is up to date', name)
83 else:
84 self.log('%s needs fetching', name)
85
86
87
88
89 toFetch.append(name)
90 if toFetch:
91 return annotated(self.callRemote('getBundleZips', toFetch),
92 toFetch, sums)
93 else:
94 return {}, [], sums
95
96 def unpackAndRegister((zips, toFetch, sums)):
97 for name in toFetch:
98 if name not in zips:
99 msg = "Missing bundle %s was not received"
100 self.warning(msg, name)
101 raise errors.NoBundleError(msg % name)
102
103 b = bundle.Bundle(name)
104 b.setZip(zips[name])
105 path = self._unbundler.unbundle(b)
106
107
108 sums.reverse()
109 ret = []
110 for name, md5 in sums:
111 self.log('registerPackagePath for %s' % name)
112 path = os.path.join(configure.cachedir, name, md5)
113 if not os.path.exists(path):
114 self.warning("path %s for bundle %s does not exist",
115 path, name)
116 else:
117 package.getPackager().registerPackagePath(path, name)
118 ret.append((name, path))
119
120 return ret
121
122
123 d = self.callRemote('getBundleSums', **kwargs)
124 d.addCallback(getZips)
125 d.addCallback(unpackAndRegister)
126 return d
127
129 """
130 Load the module given by name.
131 Sets up all necessary bundles to be able to load the module.
132
133 @rtype: L{twisted.internet.defer.Deferred}
134 @returns: a deferred that will fire when the given module is loaded,
135 giving the loaded module.
136 """
137
138 def gotBundles(bundles):
139 self.debug('Got bundles %r', bundles)
140
141
142 __import__(moduleName, globals(), locals(), [])
143 self.log('loaded module %s', moduleName)
144 return sys.modules[moduleName]
145
146 self.debug('Loading module %s', moduleName)
147
148
149 d = self.getBundles(moduleName=moduleName)
150 d.addCallback(gotBundles)
151 return d
152
154 """
155 Get the given bundle locally.
156
157 @rtype: L{twisted.internet.defer.Deferred}
158 @returns: a deferred returning the absolute path under which the
159 bundle is extracted.
160 """
161
162 def gotBundles(bundles):
163 name, path = bundles[-1]
164 assert name == bundleName
165 self.debug('Got bundle %s in %s', bundleName, path)
166 return path
167
168
169 self.debug('Getting bundle %s', bundleName)
170 d = self.getBundles(bundleName=bundleName)
171 d.addCallback(gotBundles)
172 return d
173
175 """
176 Do everything needed to get the given bundled file.
177
178 @returns: a deferred returning the absolute path to a local copy
179 of the given file.
180 """
181
182 def gotBundles(bundles):
183 name, bundlePath = bundles[-1]
184 path = os.path.join(bundlePath, fileName)
185 if not os.path.exists(path):
186 self.warning("path %s for file %s does not exist",
187 path, fileName)
188 return path
189
190 self.debug('Getting file %s', fileName)
191 d = self.getBundles(fileName=fileName)
192 d.addCallback(gotBundles)
193 return d
194