1 """
2 Classes associated with dynamic module loading
3
4 Copyright: John Stowers, 2006
5 License: GPLv2
6 """
7
8 import gobject
9 import os, os.path
10 import traceback
11 import pydoc
12 import logging
13 log = logging.getLogger("Module")
14
15 import conduit.dataproviders
16 import conduit.ModuleWrapper as ModuleWrapper
17 import conduit.Vfs as Vfs
18
20 """
21 Generic dynamic module loader for conduit. Given a path
22 it loads all modules in that directory, keeping them in an
23 internam array which may be returned via get_modules
24
25 Also manages dataprovider factories which make dataproviders available
26 at runtime
27 """
28 __gsignals__ = {
29
30
31 "dataprovider-available" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [
32 gobject.TYPE_PYOBJECT]),
33 "dataprovider-unavailable" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [
34 gobject.TYPE_PYOBJECT]),
35
36 "all-modules-loaded" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, []),
37
38 "syncset-added" : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, [
39 gobject.TYPE_PYOBJECT]),
40 }
41
43 """
44 @param dirs: A list of directories to search. Relative pathnames and paths
45 containing ~ will be expanded. If dirs is None the
46 ModuleLoader will not search for modules.
47 @type dirs: C{string[]}
48 """
49 gobject.GObject.__init__(self)
50
51
52
53 self.moduleWrappers = {}
54
55 self.invalidFiles = []
56
57 self.dataproviderFactories = []
58
59 self.filelist = self._build_filelist_from_directories(dirs)
60
62 """
63 Store the ipod so it can be retrieved later by the treeview/model
64 emit a signal so it is added to the GUI
65 """
66 log.info("Dynamic dataprovider (%s) available by %s" % (dpw, monitor))
67 self._append_module(dpw, klass)
68
70 log.info("Dynamic dataprovider (%s) unavailable by %s" % (key, monitor))
71 self._remove_module(key)
72
74 if dataproviderWrapper.module_type in ["source", "sink", "twoway"]:
75 self.emit("dataprovider-available", dataproviderWrapper)
76
78 if dataproviderWrapper.module_type in ["source", "sink", "twoway"]:
79 self.emit("dataprovider-unavailable", dataproviderWrapper)
80
82 """
83 Converts a given array of directories into a list
84 containing the filenames of all qualified modules. Recurses into
85 directories and adds files if they have the same name as the
86 directory in which they reside.
87 This method is automatically invoked by the constructor.
88 """
89 res = []
90 if not directories:
91 return res
92
93
94 directories = [os.path.abspath(os.path.expanduser(s)) for s in directories]
95
96 while len(directories) > 0:
97 d = directories.pop(0)
98 try:
99 if not os.path.exists(d):
100 continue
101 for i in os.listdir(d):
102 f = os.path.join(d,i)
103 if os.path.isfile(f) and self._is_module(f):
104 if os.path.basename(f) not in [os.path.basename(j) for j in res]:
105 res.append(f)
106 elif os.path.isdir(f) and self._is_module_dir(f):
107 directories.append(f)
108 except OSError, err:
109 log.warn("Error reading directory %s, skipping." % (d))
110 return res
111
113 return filename.endswith("Module.py")
114
116 return dirname.endswith("Module")
117
119
120 key = wrapper.get_dnd_key()
121 if key not in self.moduleWrappers:
122 self.moduleWrappers[key] = wrapper
123
124 self._emit_available(wrapper)
125 else:
126 log.warn("Wrapper with key %s allready loaded" % key)
127
129 """
130 Looks for a given key in the class registry and attempts to remove it
131
132 @param key: The key of the class to remove
133 """
134
135 if not key in self.moduleWrappers:
136 log.warn("Unable to remove class - it isn't available! (%s)" % key)
137 return
138
139
140 dpw = self.moduleWrappers[key]
141
142
143 del self.moduleWrappers[key]
144
145 self._emit_unavailable(dpw)
146
148 """
149 Tries to import the specified file. Returns the python module on succes.
150 Primarily for internal use. Note that the python module returned may actually
151 contain several more loadable modules.
152 """
153 mods = pydoc.importfile (filename)
154 try:
155 if (mods.MODULES): pass
156 except AttributeError:
157 log.warn("The file %s is not a valid module. Skipping." % (filename))
158 log.warn("A module must have the variable MODULES defined as a dictionary.")
159 raise
160 for modules, infos in mods.MODULES.items():
161 for i in ModuleWrapper.COMPULSORY_ATTRIBUTES:
162 if i not in infos:
163 log.warn("Class %s in file %s does define a %s attribute. Skipping." % (modules, filename, i))
164 raise Exception
165 return mods
166
168 """
169 Loads all modules in the given file
170 """
171 try:
172 mod = self._import_file(filename)
173 for modules, infos in mod.MODULES.items():
174 try:
175 klass = getattr(mod, modules)
176 if infos["type"] == "dataprovider" or infos["type"] == "converter":
177 mod_wrapper = ModuleWrapper.ModuleWrapper(
178 klass=klass,
179 initargs=(),
180 category=getattr(klass, "_category_", conduit.dataproviders.CATEGORY_TEST)
181 )
182
183 self._append_module(
184 mod_wrapper,
185 klass
186 )
187 elif infos["type"] == "dataprovider-factory":
188
189 kwargs = {
190 "moduleManager": self,
191 }
192
193 instance = klass(**kwargs)
194 self.dataproviderFactories.append(instance)
195 else:
196 log.warn("Class is an unknown type: %s" % klass)
197 except AttributeError:
198 log.warn("Could not find module %s in %s\n%s" % (modules,filename,traceback.format_exc()))
199 except Exception, e:
200 log.warn("Error loading the file: %s\n%s" % (filename, traceback.format_exc()))
201 self.invalidFiles.append(os.path.basename(filename))
202
203 - def load_all(self, whitelist, blacklist):
227
229 """
230 @returns: All loaded modules
231 @rtype: L{conduit.ModuleManager.ModuleWrapper}[]
232 """
233 return self.moduleWrappers.values()
234
236 """
237 Returns all loaded modules of type specified by type_filter
238 or all if the filter is set to None.
239 """
240 if len(type_filter) == 0:
241 return self.moduleWrappers.values()
242 return [i for i in self.moduleWrappers.values() if i.module_type in type_filter]
243
245 """
246 Returns a new ModuleWrapper with a dp instace described by wrapperKey
247 """
248 mod_wrapper = None
249
250 if wrapperKey in self.moduleWrappers:
251
252 m = self.moduleWrappers[wrapperKey]
253
254 mod_wrapper = ModuleWrapper.ModuleWrapper(
255 klass=m.klass,
256 initargs=m.initargs,
257 category=m.category
258 )
259 mod_wrapper.instantiate_module()
260 else:
261 log.warn("Could not find module wrapper: %s" % (wrapperKey))
262 mod_wrapper = ModuleWrapper.PendingDataproviderWrapper(wrapperKey)
263
264 return mod_wrapper
265
267 """
268 If it is necesary to call the modules directly. This
269 function creates those instances in wrappers of the specified type
270 """
271 for i in self.moduleWrappers.values():
272 if i.module_type == type_filter:
273 i.instantiate_module()
274
276 for dpf in self.dataproviderFactories:
277 dpf.quit()
278