1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 """
23 Bundle fetching, caching, and importing utilities for clients using
24 bundled code and data
25 """
26
27 import os
28 import sys
29
30 from twisted.internet import error, defer
31
32 from flumotion.common import bundle, common, errors, log, package
33 from flumotion.configure import configure
34
35 __all__ = ['BundleLoader']
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 def annotated(d, *extraVals):
68 def annotatedReturn(ret):
69 return (ret,) + extraVals
70 d.addCallback(annotatedReturn)
71 return d
72
73 def getZips(sums):
74
75
76 toFetch = []
77 for name, md5 in sums:
78 path = os.path.join(configure.cachedir, name, md5)
79 if os.path.exists(path):
80 self.log('%s is up to date', name)
81 else:
82 self.log('%s needs fetching', name)
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 def gotBundles(bundles):
132 self.debug('Got bundles %r', bundles)
133
134
135 __import__(moduleName, globals(), locals(), [])
136 self.log('loaded module %s', moduleName)
137 return sys.modules[moduleName]
138
139 self.debug('Loading module %s', moduleName)
140
141
142 d = self.getBundles(moduleName=moduleName)
143 d.addCallback(gotBundles)
144 return d
145
147 """
148 Get the given bundle locally.
149
150 @rtype: L{twisted.internet.defer.Deferred}
151 @returns: a deferred returning the absolute path under which the
152 bundle is extracted.
153 """
154 def gotBundles(bundles):
155 name, path = bundles[-1]
156 assert name == bundleName
157 self.debug('Got bundle %s in %s', bundleName, path)
158 return path
159
160
161 self.debug('Getting bundle %s', bundleName)
162 d = self.getBundles(bundleName=bundleName)
163 d.addCallback(gotBundles)
164 return d
165
167 """
168 Do everything needed to get the given bundled file.
169
170 @returns: a deferred returning the absolute path to a local copy
171 of the given file.
172 """
173 def gotBundles(bundles):
174 name, bundlePath = bundles[-1]
175 path = os.path.join(bundlePath, fileName)
176 if not os.path.exists(path):
177 self.warning("path %s for file %s does not exist",
178 path, fileName)
179 return path
180
181 self.debug('Getting file %s', fileName)
182 d = self.getBundles(fileName=fileName)
183 d.addCallback(gotBundles)
184 return d
185