1 #=======================================================================
3 __version__
= '''0.1.02'''
4 __sub_version__
= '''20051204044022'''
5 __copyright__
= '''(c) Alex A. Nannou 2004'''
11 #-----------------------------------------------------------------------
17 import pli
.importutils
as importutils
18 import pli
.event
as event
19 import pli
.misc
.extendedtypes
as exttypes
22 #-----------------------------------------------------------------------
23 #-----------------------------------------------PluginDependencyError---
24 class PluginDependencyError(Exception):
30 #--------------------------------------------------------onPluginLoad---
31 class onPluginLoad(event
.Event
):
34 the callback will recive the name of the plugin package and the
40 #-----------------------------------------------------------------------
41 # TODO a more basic version with method callbacks intead of events...
43 # TODO cleanup code...
44 #-------------------------------------------------------------Plugins---
45 class Plugins(object):
48 __packageiter__
= staticmethod(importutils
.packageiter
)
50 def __init__(self
, path
=None, prefix
=None):
54 # by default get path relative to the module this was
55 # called from.... (????)
56 # (e.g. import all that is on callres level...)
59 f_locals
= sys
._getframe
(frame_depth
+i
).f_locals
60 if '__file__' in f_locals
:
63 path
= os
.path
.dirname(f_locals
['__file__'])
65 # the prefix is same as caller...
66 n
= f_locals
['__name__']
69 elif type(path
) == types
.ModuleType
:
72 path
= os
.path
.dirname(mod
.__file
__)
73 if prefix
== None and mod
.__name
__ != '__main__':
76 elif exttypes
.isstring(path
):
80 # normalize the path...
81 f
= os
.path
.normpath(path
)
83 # see if it is in sys.modules...
84 for n
, v
in sys
.modules
.iteritems():
85 if hasattr(v
, '__file__') and v
.__file
__.startswith(f
) and v
.__name
__ != None:
88 # Q: is there anythong else we could do here???
91 raise TypeError, 'the path may either be a string (or unicode), a package or None (got: %s).' % path
97 self
.sorted_plugins
= None
99 # custumization methods...
100 def __getplugins__(self
):
102 this will get a list of loadable plugins.
104 NOTE: this will override the old list.
105 NOTE: this will actually import all the found modules, regardless of
106 if they are plugins or not.
109 plugins
= self
.plugins
111 isplugin
= self
.__isplugin
__
112 for n
in self
.__packageiter
__(self
.path
):
113 # import the plugin...
114 name
= prefix
+ '.' + n
115 ##!!! correct the context...
117 for nn
in name
.split('.')[1:]:
119 # check the module...
125 # the folowing are stubs...
126 def __isplugin__(self
, obj
):
128 this will test a plugin for compliancy.
130 NOTE: this will not load the plugin.
133 def __cmpplugins__(self
, a
, b
):
138 # NOTE: this should call the onPluginLoad event...
139 def __loadplugin__(self
, plugin
):
142 raise NotImplementedError
143 def __unloadplugin__(self
, plugin
):
146 raise NotImplementedError
148 def __sortplugins__(self
):
151 plugins
= self
.sorted_plugins
= self
.plugins
.values()
154 plugins
.sort(cmp=self
.__cmpplugins
__)
159 this will read the plugins, their configuration and dependencies.
161 self
.__getplugins
__()
162 self
.__sortplugins
__()
163 # XXX do more checks... (are the plugins read before this??)
166 this will load the plugins in order of dependency.
168 load
= self
.__loadplugin
__
169 for p
in self
.sorted_plugins
:
171 # XXX should the order of the unload be reversed???
175 load
= self
.__unloadplugin
__
176 for p
in self
.sorted_plugins
:
179 # interface methods...
183 #-----------------------------------------------PluginsDependencyErro---
184 class PluginsDependencyErro(Exception):
190 #---------------------------------------------PluginsWithDependencies---
191 class PluginsWithDependencies(Plugins
):
194 # NOTE: __plugindepends__ and __builddependencies__ are called
195 # AFTER all plugins are imported...
196 def __plugindepends__(self
, plugin
):
198 this will get object dependencies.
200 NOTE: this should only provide direct dependencies.
203 def __builddependencies__(self
):
205 this will build the dependency tree.
207 NOTE: this will break if a dependency loop is detected.
209 res
= self
._dependency
_cache
= {}
210 getdeps
= self
.__plugindepends
__
211 for plugin
in self
.plugins
.values():
212 # NOTE: here we will exclude the root from the tree...
214 deps
= res
[plugin
] = list(itertree(plugin
, getdeps
))[1:]
216 def __cmpplugins__(self
, a
, b
):
220 if b
in self
._dependency
_cache
[a
]:
222 if a
in self
._dependency
_cache
[b
]:
224 raise PluginDependencyError
, 'cyclic dependency error (%s, %s).' % (a
, b
)
227 def __sortplugins__(self
):
230 self
.__builddependencies
__()
231 super(PluginsWithDependencies
, self
).__sortplugins
__()
241 #-----------------------------------------------------------------------
242 ##!!! MOVE THIS TO A SEPORATE MODULE...
243 #------------------------------------------------------------TreeErro---
244 class TreeError(Exception):
250 #-----------------------------------------------------------------------
257 #------------------------------------------------------------itertree---
258 def itertree(tree
, getchildren
=None, mode
=WALK_BREADTH
, onloop
=LOOP_ERR
):
260 return a list of nodes of a tree.
264 l
= getchildren(tree
)
269 if onloop
== LOOP_ERR
:
270 raise TreeError
, 'loop detected.'
271 # ignore the looping branch and continue...
272 if onLoop
== LOOP_IGNORE
:
275 raise TypeError, 'unknown loop handler event code.'
278 if mode
== WALK_BREADTH
:
280 elif mode
== WALK_DEPTH
:
281 l
[:0] = getchildren(c
)
283 raise TypeError, 'unknown mode (%s).' % mode
287 #-----------------------------------------------------------------------
288 if __name__
== '__main__':
290 t
= [((1, 2), (3, (4, 5)),), 6]
300 print list(itertree(t
, gc
, WALK_BREADTH
))
301 print list(itertree(t
, gc
, WALK_DEPTH
))
306 #=======================================================================
307 # vim:set ts=4 sw=4 nowrap :