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