|
@@ -33,7 +33,7 @@ LodelContext.expose_modules(globals(), {
|
33
|
33
|
#
|
34
|
34
|
#@todo Check if the symlink in the lodelcontext dir is obsolete !!!
|
35
|
35
|
#@warning The plugins dir is at two locations : in lodel package and in
|
36
|
|
-#instance directory. Some stuff seems to still needs plugins to be in
|
|
36
|
+#instance directory. Some stuff seems to still needs plugins to be in
|
37
|
37
|
#the instance directory but it seems to be a really bad idea...
|
38
|
38
|
|
39
|
39
|
##@defgroup plugin_init_specs Plugins __init__.py specifications
|
|
@@ -67,7 +67,7 @@ PLUGINS_PATH = os.path.join(LodelContext.context_dir(),'plugins')
|
67
|
67
|
|
68
|
68
|
##@brief List storing the mandatory variables expected in a plugin __init__.py
|
69
|
69
|
#file
|
70
|
|
-MANDATORY_VARNAMES = [PLUGIN_NAME_VARNAME, LOADER_FILENAME_VARNAME,
|
|
70
|
+MANDATORY_VARNAMES = [PLUGIN_NAME_VARNAME, LOADER_FILENAME_VARNAME,
|
71
|
71
|
PLUGIN_VERSION_VARNAME]
|
72
|
72
|
|
73
|
73
|
##@brief Default plugin type
|
|
@@ -122,7 +122,7 @@ version number" % arg)
|
122
|
122
|
elif len(args) > 3:
|
123
|
123
|
raise PluginError("Expected between 1 and 3 positional arguments \
|
124
|
124
|
but %d arguments found" % len(args))
|
125
|
|
- else:
|
|
125
|
+ else:
|
126
|
126
|
for i,v in enumerate(args):
|
127
|
127
|
self.__version[i] = int(v)
|
128
|
128
|
#Checks that version numbering is correct
|
|
@@ -144,7 +144,7 @@ but %d arguments found" % len(args))
|
144
|
144
|
@property
|
145
|
145
|
def revision(self):
|
146
|
146
|
return self.__version[2]
|
147
|
|
-
|
|
147
|
+
|
148
|
148
|
##@brief Check and prepare comparisoon argument
|
149
|
149
|
#@return A PluginVersion instance
|
150
|
150
|
#@throw PluginError if invalid argument provided
|
|
@@ -157,7 +157,7 @@ but %d arguments found" % len(args))
|
157
|
157
|
raise PluginError("Cannot compare argument '%s' with \
|
158
|
158
|
a PluginVerison instance" % other)
|
159
|
159
|
return other
|
160
|
|
-
|
|
160
|
+
|
161
|
161
|
##@brief Allow accessing to versions parts using interger index
|
162
|
162
|
#@param key int : index
|
163
|
163
|
#@return major for key == 0, minor for key == 1, revision for key == 2
|
|
@@ -213,7 +213,7 @@ a PluginVerison instance" % other)
|
213
|
213
|
#
|
214
|
214
|
#Automatic script registration on child class declaration
|
215
|
215
|
class MetaPlugType(type):
|
216
|
|
-
|
|
216
|
+
|
217
|
217
|
##@brief Dict storing all plugin types
|
218
|
218
|
#
|
219
|
219
|
#key is the _type_conf_name and value is the class
|
|
@@ -227,20 +227,20 @@ class MetaPlugType(type):
|
227
|
227
|
return
|
228
|
228
|
#Regitering a new plugin type
|
229
|
229
|
MetaPlugType._all_ptypes[self._type_conf_name] = self
|
230
|
|
-
|
|
230
|
+
|
231
|
231
|
##@brief Accessor to the list of plugin types
|
232
|
232
|
#@return A copy of _all_ptypes attribute (a dict with typename as key
|
233
|
233
|
#and the class as value)
|
234
|
234
|
@classmethod
|
235
|
235
|
def all_types(cls):
|
236
|
236
|
return copy.copy(cls._all_ptypes)
|
237
|
|
-
|
|
237
|
+
|
238
|
238
|
##@brief Accessor to the list of plugin names
|
239
|
239
|
#@return a list of plugin name
|
240
|
240
|
@classmethod
|
241
|
241
|
def all_ptype_names(cls):
|
242
|
242
|
return list(cls._all_ptypes.keys())
|
243
|
|
-
|
|
243
|
+
|
244
|
244
|
##@brief Given a plugin type name return a Plugin child class
|
245
|
245
|
#@param ptype_name str : a plugin type name
|
246
|
246
|
#@return A Plugin child class
|
|
@@ -250,13 +250,13 @@ class MetaPlugType(type):
|
250
|
250
|
if ptype_name not in cls._all_ptypes:
|
251
|
251
|
raise PluginError("Unknown plugin type '%s'" % ptype_name)
|
252
|
252
|
return cls._all_ptypes[ptype_name]
|
253
|
|
-
|
|
253
|
+
|
254
|
254
|
##@brief Call the clear classmethod on each child classes
|
255
|
255
|
@classmethod
|
256
|
256
|
def clear_cls(cls):
|
257
|
257
|
for pcls in cls._all_ptypes.values():
|
258
|
258
|
pcls.clear_cls()
|
259
|
|
-
|
|
259
|
+
|
260
|
260
|
|
261
|
261
|
##@brief Handle plugins
|
262
|
262
|
#@ingroup lodel2_plugins
|
|
@@ -269,17 +269,17 @@ class MetaPlugType(type):
|
269
|
269
|
# 2. Settings fetch all confspecs
|
270
|
270
|
# 3. the loader call load_all to register hooks etc
|
271
|
271
|
class Plugin(object, metaclass=MetaPlugType):
|
272
|
|
-
|
|
272
|
+
|
273
|
273
|
##@brief Stores Plugin instances indexed by name
|
274
|
274
|
_plugin_instances = dict()
|
275
|
|
-
|
|
275
|
+
|
276
|
276
|
##@brief Attribute used by load_all and load methods to detect circular
|
277
|
277
|
#dependencies
|
278
|
278
|
_load_called = []
|
279
|
279
|
|
280
|
280
|
##@brief Attribute that stores plugins list from discover cache file
|
281
|
281
|
_plugin_list = None
|
282
|
|
-
|
|
282
|
+
|
283
|
283
|
#@brief Designed to store, in child classes, the confspec indicating \
|
284
|
284
|
#where plugin list is stored
|
285
|
285
|
_plist_confspecs = None
|
|
@@ -303,12 +303,12 @@ class Plugin(object, metaclass=MetaPlugType):
|
303
|
303
|
# @param plugin_name str : plugin name
|
304
|
304
|
# @throw PluginError
|
305
|
305
|
def __init__(self, plugin_name):
|
306
|
|
-
|
|
306
|
+
|
307
|
307
|
##@brief The plugin name
|
308
|
308
|
self.name = plugin_name
|
309
|
309
|
##@brief The plugin package path
|
310
|
310
|
self.path = self.plugin_path(plugin_name)
|
311
|
|
-
|
|
311
|
+
|
312
|
312
|
##@brief Stores the plugin module
|
313
|
313
|
self.module = None
|
314
|
314
|
##@brief Stores the plugin loader module
|
|
@@ -317,7 +317,7 @@ class Plugin(object, metaclass=MetaPlugType):
|
317
|
317
|
self.__confspecs = dict()
|
318
|
318
|
##@brief Boolean flag telling if the plugin is loaded or not
|
319
|
319
|
self.loaded = False
|
320
|
|
-
|
|
320
|
+
|
321
|
321
|
# Importing __init__.py infos in it
|
322
|
322
|
plugin_module = self.module_name()
|
323
|
323
|
self.module = LodelContext.module(plugin_module)
|
|
@@ -398,7 +398,7 @@ name differ from the one found in plugin's init file"
|
398
|
398
|
#@throw PluginError if the filename was not valid
|
399
|
399
|
#@todo Some strange things append :
|
400
|
400
|
#when loading modules in test self.module.__name__ does not contains
|
401
|
|
- #the package... but in prod cases the self.module.__name__ is
|
|
401
|
+ #the package... but in prod cases the self.module.__name__ is
|
402
|
402
|
#the module fullname... Just a reminder note to explain the dirty
|
403
|
403
|
#if on self_modname
|
404
|
404
|
def _import_from_init_var(self, varname):
|
|
@@ -428,9 +428,12 @@ name differ from the one found in plugin's init file"
|
428
|
428
|
base_mod = '.'.join(filename.split('.')[:-1])
|
429
|
429
|
module_name = self_modname+"."+base_mod
|
430
|
430
|
return importlib.import_module(module_name)
|
431
|
|
-
|
|
431
|
+
|
432
|
432
|
##@brief Return associated module name
|
433
|
433
|
def module_name(self):
|
|
434
|
+ #WOOT WE LOVE DIRTY WORKAROUNDS !!!
|
|
435
|
+ return "lodel.plugins."+os.path.basename(self.path)
|
|
436
|
+ #END OF DIRTY WORKAROUND
|
434
|
437
|
if not self.path.startswith('./plugins'):
|
435
|
438
|
raise PluginError("Bad path for plugin %s : %s" % (
|
436
|
439
|
self.name, self.path))
|
|
@@ -460,7 +463,7 @@ name differ from the one found in plugin's init file"
|
460
|
463
|
raise PluginError( "Bad dependencie for '%s' :"%self.name,
|
461
|
464
|
', '.join(errors))
|
462
|
465
|
return result
|
463
|
|
-
|
|
466
|
+
|
464
|
467
|
##@brief Check if the plugin should be activated
|
465
|
468
|
#
|
466
|
469
|
#Try to fetch a function called @ref ACTIVATE_METHOD_NAME in __init__.py
|
|
@@ -478,10 +481,10 @@ name differ from the one found in plugin's init file"
|
478
|
481
|
logger.debug(msg)
|
479
|
482
|
test_fun = lambda:True
|
480
|
483
|
return test_fun()
|
481
|
|
-
|
|
484
|
+
|
482
|
485
|
##@brief Load a plugin
|
483
|
486
|
#
|
484
|
|
- #Loading a plugin means importing a file. The filename is defined in the
|
|
487
|
+ #Loading a plugin means importing a file. The filename is defined in the
|
485
|
488
|
#plugin's __init__.py file in a LOADER_FILENAME_VARNAME variable.
|
486
|
489
|
#
|
487
|
490
|
#The loading process has to take care of other things :
|
|
@@ -503,7 +506,7 @@ name differ from the one found in plugin's init file"
|
503
|
506
|
raise PluginError("Circular dependencie in Plugin detected. Abording")
|
504
|
507
|
else:
|
505
|
508
|
self._load_called.append(self.name)
|
506
|
|
-
|
|
509
|
+
|
507
|
510
|
#Dependencie load
|
508
|
511
|
for dependencie in self.check_deps():
|
509
|
512
|
activable = dependencie.activable()
|
|
@@ -515,7 +518,7 @@ name differ from the one found in plugin's init file"
|
515
|
518
|
plugin_name = self.name,
|
516
|
519
|
dep_name = dependencie.name,
|
517
|
520
|
reason = activable)
|
518
|
|
-
|
|
521
|
+
|
519
|
522
|
#Loading the plugin
|
520
|
523
|
try:
|
521
|
524
|
self.__loader_module = self._import_from_init_var(LOADER_FILENAME_VARNAME)
|
|
@@ -529,7 +532,7 @@ name differ from the one found in plugin's init file"
|
529
|
532
|
raise PluginError(msg)
|
530
|
533
|
logger.debug("Plugin '%s' loaded" % self.name)
|
531
|
534
|
self.loaded = True
|
532
|
|
-
|
|
535
|
+
|
533
|
536
|
##@brief Returns the loader module
|
534
|
537
|
#
|
535
|
538
|
#Accessor for the __loader__ python module
|
|
@@ -564,7 +567,7 @@ name differ from the one found in plugin's init file"
|
564
|
567
|
raise PluginError(msg)
|
565
|
568
|
LodelHook.call_hook(
|
566
|
569
|
"lodel2_plugins_loaded", cls, cls._plugin_instances)
|
567
|
|
-
|
|
570
|
+
|
568
|
571
|
|
569
|
572
|
##@return a copy of __confspecs attr
|
570
|
573
|
@property
|
|
@@ -598,7 +601,7 @@ name differ from the one found in plugin's init file"
|
598
|
601
|
return res
|
599
|
602
|
|
600
|
603
|
##@brief Register a new plugin
|
601
|
|
- #
|
|
604
|
+ #
|
602
|
605
|
#@param plugin_name str : The plugin name
|
603
|
606
|
#@return a Plugin instance
|
604
|
607
|
#@throw PluginError
|
|
@@ -632,7 +635,7 @@ name differ from the one found in plugin's init file"
|
632
|
635
|
msg = "No plugin named '%s' loaded"
|
633
|
636
|
msg %= plugin_name
|
634
|
637
|
raise PluginError(msg)
|
635
|
|
-
|
|
638
|
+
|
636
|
639
|
##@brief Given a plugin name returns the plugin path
|
637
|
640
|
# @param plugin_name str : The plugin name
|
638
|
641
|
# @return the plugin directory path
|
|
@@ -648,7 +651,7 @@ name differ from the one found in plugin's init file"
|
648
|
651
|
pass
|
649
|
652
|
|
650
|
653
|
return plist[plugin_name]['path']
|
651
|
|
-
|
|
654
|
+
|
652
|
655
|
##@brief Return the plugin module name
|
653
|
656
|
#
|
654
|
657
|
#This module name is the "virtual" module where we imported the plugin.
|
|
@@ -664,7 +667,7 @@ name differ from the one found in plugin's init file"
|
664
|
667
|
return "%s.%s" % (VIRTUAL_PACKAGE_NAME, plugin_name)
|
665
|
668
|
|
666
|
669
|
##@brief Start the Plugin class
|
667
|
|
- #
|
|
670
|
+ #
|
668
|
671
|
# Called by Settings.__bootstrap()
|
669
|
672
|
#
|
670
|
673
|
# This method load path and preload plugins
|
|
@@ -672,7 +675,7 @@ name differ from the one found in plugin's init file"
|
672
|
675
|
def start(cls, plugins):
|
673
|
676
|
for plugin_name in plugins:
|
674
|
677
|
cls.register(plugin_name)
|
675
|
|
-
|
|
678
|
+
|
676
|
679
|
##@brief Attempt to "restart" the Plugin class
|
677
|
680
|
@classmethod
|
678
|
681
|
def clear(cls):
|
|
@@ -681,12 +684,12 @@ name differ from the one found in plugin's init file"
|
681
|
684
|
if cls._load_called != []:
|
682
|
685
|
cls._load_called = []
|
683
|
686
|
MetaPlugType.clear_cls()
|
684
|
|
-
|
|
687
|
+
|
685
|
688
|
##@brief Designed to be implemented by child classes
|
686
|
689
|
@classmethod
|
687
|
690
|
def clear_cls(cls):
|
688
|
691
|
pass
|
689
|
|
-
|
|
692
|
+
|
690
|
693
|
##@brief Reccursively walk throught paths to find plugin, then stores
|
691
|
694
|
#found plugin in a static var
|
692
|
695
|
#
|
|
@@ -704,7 +707,7 @@ name differ from the one found in plugin's init file"
|
704
|
707
|
result = dict()
|
705
|
708
|
for pinfos in tmp_res:
|
706
|
709
|
pname = pinfos['name']
|
707
|
|
- if ( pname in result
|
|
710
|
+ if ( pname in result
|
708
|
711
|
and pinfos['version'] > result[pname]['version'])\
|
709
|
712
|
or pname not in result:
|
710
|
713
|
result[pname] = pinfos
|
|
@@ -713,7 +716,7 @@ name differ from the one found in plugin's init file"
|
713
|
716
|
pass
|
714
|
717
|
cls._plugin_list = result
|
715
|
718
|
return result
|
716
|
|
-
|
|
719
|
+
|
717
|
720
|
##@brief Return discover result
|
718
|
721
|
#@param refresh bool : if true invalidate all plugin list cache
|
719
|
722
|
#@note If discover cache file not found run discover first
|
|
@@ -785,7 +788,7 @@ name differ from the one found in plugin's init file"
|
785
|
788
|
'version': PluginVersion(pversion),
|
786
|
789
|
'path': path,
|
787
|
790
|
'type': ptype}
|
788
|
|
-
|
|
791
|
+
|
789
|
792
|
##@brief Import init file from a plugin path
|
790
|
793
|
#@param path str : Directory path
|
791
|
794
|
#@return a tuple (init_module, module_name)
|
|
@@ -862,7 +865,7 @@ class CustomMethod(object):
|
862
|
865
|
##@brief Decorator constructor
|
863
|
866
|
#@param component_name str : the name of the component to enhance
|
864
|
867
|
#@param method_name str : the name of the method to inject (if None given
|
865
|
|
- #@param method_type int : take value in one of
|
|
868
|
+ #@param method_type int : take value in one of
|
866
|
869
|
#CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or
|
867
|
870
|
#CustomMethod::STATIC_METHOD
|
868
|
871
|
#use the function name
|
|
@@ -880,7 +883,7 @@ class CustomMethod(object):
|
880
|
883
|
raise ValueError("Excepted value for method_type was one of \
|
881
|
884
|
CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or \
|
882
|
885
|
CustomMethod::STATIC_METHOD, but got %s" % self._type)
|
883
|
|
-
|
|
886
|
+
|
884
|
887
|
##@brief called just after __init__
|
885
|
888
|
#@param fun function : the decorated function
|
886
|
889
|
def __call__(self, fun):
|
|
@@ -896,7 +899,7 @@ another plugin : %s" % (
|
896
|
899
|
self._custom_methods[self._comp_name].__module__))
|
897
|
900
|
self._fun = fun
|
898
|
901
|
self._custom_methods[self._comp_name].append(self)
|
899
|
|
-
|
|
902
|
+
|
900
|
903
|
##@brief Textual representation
|
901
|
904
|
#@return textual representation of the CustomMethod instance
|
902
|
905
|
def __repr__(self):
|
|
@@ -907,7 +910,7 @@ source={module_name}.{fun_name}>"
|
907
|
910
|
classname = self._comp_name,
|
908
|
911
|
module_name = self._fun.__module__,
|
909
|
912
|
fun_name = self._fun.__name__)
|
910
|
|
-
|
|
913
|
+
|
911
|
914
|
##@brief Return a well formed method
|
912
|
915
|
#
|
913
|
916
|
#@note the type of method depends on the _type attribute
|
|
@@ -929,7 +932,7 @@ CustomMethod::STATIC_METHOD")
|
929
|
932
|
|
930
|
933
|
##@brief Handle custom method dynamic injection in LeAPI dyncode
|
931
|
934
|
#
|
932
|
|
- #Called by lodel2_dyncode_loaded hook defined at
|
|
935
|
+ #Called by lodel2_dyncode_loaded hook defined at
|
933
|
936
|
#lodel.plugin.core_hooks.lodel2_plugin_custom_methods()
|
934
|
937
|
#
|
935
|
938
|
#@param cls
|