2 # -*- coding: utf-8 -*-
3 # vim: expandtab:shiftwidth=4:fileencoding=utf-8 :
5 # Copyright ® 2008 Fulvio Satta
7 # If you want contact me, send an email to Yota_VGA@users.sf.net
9 # This file is part of jcd
11 # jcd is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
16 # jcd is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # GNU General Public License for more details.
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #TODO: Test, test, test
28 #TODO: Complete the syncronization part
29 #TODO: Manage the plugin unloading correctly
30 #TODO: Complete the reloading part
32 ##########################
33 ##### IMPORT SECTION #####
34 ##########################
36 import warnings
as _warnings
38 from Syncronized
import AutoLock
as _AutoLock
39 from Syncronized
import RWAutoLock
as _RWAutoLock
40 from Syncronized
import RWSyncronized
as _RWSyncronized
42 from SignalSlot
import SignalBase
as _SignalBase
44 from threading
import RLock
as _RLock
46 #####################################
47 ##### EXTERNAL VARIABLE SECTION #####
48 #####################################
68 ######################################
69 ##### INTERNAL FUNCTIONS SECTION #####
70 ######################################
73 def _import(plugin
, dir, plugname
):
79 fullpath
= os
.path
.join(dir, plugin
.replace(u
'.', os
.sep
))
80 path
, name
= os
.path
.split(fullpath
)
82 #If the plugin is already exported return it
84 return sys
.modules
[plugname
]
89 fp
, pathname
, description
= imp
.find_module(name
, [path
])
93 return imp
.load_module(plugname
, fp
, pathname
, description
)
98 #Load a plugin, but not init that
99 def _load(plugin
, dir):
100 from codecs
import open
102 lines
= open(plugin
, encoding
= 'utf8').readlines()
103 lines
= [line
.strip() for line
in lines
if line
[0] != u
'#' and line
.strip()]
106 module
= _import(lines
[0], dir, plugin
)
108 _warnings
.warn('Plugin %s have an import error: %s, %s, %s' % (
109 os
.path
.join(dir, plugin
),
111 ImportWarning
, stacklevel
= 2)
113 _warnings
.warn('Plugin %s have an import error' %
114 os
.path
.join(dir, plugin
),
115 ImportWarning
, stacklevel
= 2)
121 ######################################
122 ##### EXTERNAL FUNCTIONS SECTION #####
123 ######################################
125 #Auboblack the modules
127 def filterBlacks(plugins
):
133 new_black_plugins
= []
135 for plugin
in plugins
:
138 black
= hasattr(plugin
[0], 'loadable') and not plugin
.loadable(plugin
[0])
140 _warnings
.warn('Plugin %s have a loadable error: %s, %s, %s' % (
143 ImportWarning
, stacklevel
= 2)
145 _warnings
.warn('Plugin %s have a loadable error' %
147 ImportWarning
, stacklevel
= 2)
150 new_plugins
.append(plugin
)
152 black_plugins
.append(plugin
)
156 new_plugins
, new_black_plugins
= filterBlacks(new_plugins
)
158 return new_plugins
, black_plugins
+ new_black_plugins
162 def findPlugins(dirs
, numbers
, order
, blackList
= []):
163 from glob
import glob
172 #Get the plugins in the directory
173 dir = os
.path
.abspath(os
.path
.expanduser(dir))
174 plugins
= glob(os
.path
.join(dir, '*.plug'))
177 for plugin
in plugins
:
178 #If the plugin is in the black list go to the next
179 if plugin
in blackList
:
180 user_blacks
.append(plugin
)
183 #Try to load the module
184 module
= _load(plugin
, dir)
188 #Get the priority number
191 number
= module
.priority
192 except AttributeError:
195 number
= numbers
[plugin
]
199 #If an order is defined follow the order, else append the module
200 #as the last of the list
202 index
= order
.index(plugin
)
204 modules
[number
] = modules
.get(number
, []) + [module
]
206 actuals
= modules_names
.get(number
, [])
207 modules_actuals
= modules
.get(number
, [])
209 for name
in order
[:index
]:
212 modules_actuals
.insert(n
, module
)
213 actuals
.insert(n
, plugin
)
214 modules
[number
] = modules_actuals
215 modules_names
[number
] = actuals
219 #Flat the modules in a list
220 def flattingModules(modules
):
223 numbers
= modules
.keys()
226 for number
in numbers
:
227 r
+= zip(modules
[number
], [number
] * len(modules
[number
]))
231 #Remove the plugins in remove but not in mantain from sys.modules
233 def removePlugins(mantain
, remove
):
237 if module
not in mantain
:
239 del sys
.modules
[module
.__name
__]
243 #Set the directory lists and load the plugins
245 def setDirs(dirs
, pluginOrderingList
, blackList
= [], autoremotion
= True):
246 global plugins
, dir_list
, numbered_plugins
, auto_blacks
248 numbers
= dict(pluginOrderingList
)
249 order
= [i
[0] for i
in pluginOrderingList
]
250 old_plugins
= plugins
+ auto_blacks
253 numbered
= findPlugins(dirs
, numbers
, order
)
254 plugins
= flattingModules(numbered
)
257 removePlugins(plugins
, old_plugins
)
259 plugins
, auto_blacks
= filterBlacks(plugins
)
261 return [plugin
for plugin
in old_plugins
if plugin
not in plugins
]
263 #Select a group of plugins
265 def getPlugins(selector
, mantainNumbers
= False):
267 return [plugin
for plugin
in plugins
if selector(plugin
)]
268 return [plugin
[0] for plugin
in plugins
if selector(plugin
[0])]
271 #WARNING: Very low level function! You must know what you do!
277 #The original settings
278 fullpath
= plugin
.__file
__
279 path
, name
= os
.path
.split(fullpath
)
280 name
= name
.rsplit('.', 1)[0]
283 fp
, pathname
, description
= imp
.find_module(name
, [path
])
287 return imp
.load_module(plugin
.__name
__, fp
, pathname
, description
)
292 ##################################
293 ##### PLUGIN CLASSES SECTION #####
294 ##################################
297 class __implementation(_RWSyncronized
, _SignalBase
):
299 _RWSyncronized
.__init
__(self
)
300 _SignalBase
.__init
__(self
)
302 def setupSignals(self
):
303 self
.addSignal('reloadPlugins')
305 __imp
= __implementation()
308 def registerObject(self
, object):
309 self
.__imp
.slots
['reloadPlugins'].append(object.reloadPlugins
)
313 def loadPlugin(self
, plugin
):
314 if plugin
not in loaded
:
315 if hasattr(plugin
, 'init'):
317 loaded
.append(plugin
)
320 def loadPlugins(self
, plugins
):
321 for plugin
in plugins
:
322 self
.loadPlugin(plugin
)
324 #Sett all the plugins as unloadables
326 @_RWAutoLock(__imp
, write
= True)
327 def startReloading(self
):
329 self
.__imp
.oldplugins
= loaded
330 self
.__imp
.emit('reloadPlugins')
333 #Unload all the plugins non reloaded
335 @_RWAutoLock(__imp
, write
= True)
336 def stopReloading(self
):
337 for plugin
in self
.__imp
.oldplugins
:
338 if plugin
not in loaded
and hasattr(plugin
, 'deinit'):
341 #Lock plugin unloading
342 def lock(self
, wait
= None):
345 return self
.__imp
.readLock(wait
)
349 #unlock plugin unloading
351 self
.__imp
.writeLock()
354 ##############################
355 ##### DECORATORS SECTION #####
356 ##############################
358 #Decorator for Loader sincronization
359 def LockPluginUnloading(f
):
362 def function(*args
, **kwargs
):
365 return f(*args
, **kwargs
)
371 ########################
372 ##### TEST SECTION #####
373 ########################
375 if __name__
== '__main__':
376 order
= {1 : ('plugins/example.plug')}
378 setDirs(['plugins'], flattingModules(order
))
380 plugin
= getPlugins(lambda plugin
: plugin
.task
['name'] == 'ExampleTask')[0]
382 loader
.loadPlugin(plugin
)
383 loader
.loadPlugin(plugin
)
385 loader
.startReloading()
386 loader
.loadPlugin(plugin
)
387 loader
.stopReloading()