|
@@ -13,96 +13,90 @@ LodelContext.expose_modules(globals(), {
|
13
|
13
|
'lodel.settings.utils': ['SettingsError'],
|
14
|
14
|
'lodel.plugin.hooks': ['LodelHook'],
|
15
|
15
|
'lodel.plugin.exceptions': ['PluginError', 'PluginVersionError',
|
16
|
|
- 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'],
|
|
16
|
+ 'PluginTypeError', 'LodelScriptError', 'DatasourcePluginError'],
|
17
|
17
|
'lodel.exceptions': ['LodelException', 'LodelExceptions',
|
18
|
|
- 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
|
18
|
+ 'LodelFatalError', 'DataNoneValid', 'FieldValidationError']})
|
19
|
19
|
|
20
|
|
-## @package lodel.plugins Lodel2 plugins management
|
|
20
|
+# @package lodel.plugins Lodel2 plugins management
|
21
|
21
|
#@ingroup lodel2_plugins
|
22
|
22
|
#
|
23
|
23
|
# Lodel2 plugins are stored in directories
|
24
|
|
-# A typicall lodel2 plugin directory structure looks like :
|
|
24
|
+# A typical lodel2 plugin directory structure looks like :
|
25
|
25
|
# - {{__init__.py}}} containing informations like full_name, authors, licence etc.
|
26
|
26
|
# - main.py containing hooks registration etc
|
27
|
27
|
# - confspec.py containing a configuration specification dictionary named CONFSPEC
|
28
|
28
|
#
|
29
|
|
-# All plugins are expected to be found in multiple locations :
|
30
|
|
-# - in the lodel package (lodel.plugins)
|
31
|
|
-# - in the context directorie in a plugins/ dir (symlink to lodel.plugins) <-
|
32
|
|
-#this is obsolete now, since we enforce ALL plugins to be in the lodel package
|
|
29
|
+# All plugins are expected to be found in the lodel package (lodel.plugins)
|
33
|
30
|
#
|
34
|
|
-#@todo Check if the symlink in the lodelcontext dir is obsolete !!!
|
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
|
37
|
|
-#the instance directory but it seems to be a really bad idea...
|
38
|
|
-
|
39
|
|
-##@defgroup plugin_init_specs Plugins __init__.py specifications
|
|
31
|
+# @defgroup plugin_init_specs Plugins __init__.py specifications
|
40
|
32
|
#@ingroup lodel2_plugins
|
41
|
33
|
#@{
|
42
|
34
|
|
43
|
|
-##@brief The package in which we will load plugins modules
|
|
35
|
+# @brief The package in which we will load plugins modules
|
44
|
36
|
VIRTUAL_PACKAGE_NAME = 'lodel.plugins'
|
45
|
|
-##@brief The temporary package to import python sources
|
|
37
|
+# @brief The temporary package to import python sources
|
46
|
38
|
VIRTUAL_TEMP_PACKAGE_NAME = 'lodel.plugin_tmp'
|
47
|
|
-##@brief Plugin init filename
|
48
|
|
-INIT_FILENAME = '__init__.py' # Loaded with settings
|
49
|
|
-##@brief Name of the variable containing the plugin name
|
|
39
|
+# @brief Plugin init filename
|
|
40
|
+INIT_FILENAME = '__init__.py' # Loaded with settings
|
|
41
|
+# @brief Name of the variable containing the plugin name
|
50
|
42
|
PLUGIN_NAME_VARNAME = '__plugin_name__'
|
51
|
|
-##@brief Name of the variable containing the plugin type
|
|
43
|
+# @brief Name of the variable containing the plugin type
|
52
|
44
|
PLUGIN_TYPE_VARNAME = '__plugin_type__'
|
53
|
|
-##@brief Name of the variable containing the plugin version
|
|
45
|
+# @brief Name of the variable containing the plugin version
|
54
|
46
|
PLUGIN_VERSION_VARNAME = '__version__'
|
55
|
|
-##@brief Name of the variable containing the confpsec filename
|
|
47
|
+# @brief Name of the variable containing the confpsec filename
|
56
|
48
|
CONFSPEC_FILENAME_VARNAME = '__confspec__'
|
57
|
|
-##@brief Name of the variable containing the confspecs
|
|
49
|
+# @brief Name of the variable containing the confspecs
|
58
|
50
|
CONFSPEC_VARNAME = 'CONFSPEC'
|
59
|
|
-##@brief Name of the variable containing the loader filename
|
|
51
|
+# @brief Name of the variable containing the loader filename
|
60
|
52
|
LOADER_FILENAME_VARNAME = '__loader__'
|
61
|
|
-##@brief Name of the variable containing the plugin dependencies
|
|
53
|
+# @brief Name of the variable containing the plugin dependencies
|
62
|
54
|
PLUGIN_DEPS_VARNAME = '__plugin_deps__'
|
63
|
|
-##@brief Name of the optionnal activate method
|
|
55
|
+# @brief Name of the optionnal activate method
|
64
|
56
|
ACTIVATE_METHOD_NAME = '_activate'
|
65
|
|
-##@brief Default & failover value for plugins path list
|
66
|
|
-PLUGINS_PATH = os.path.join(LodelContext.context_dir(),'plugins')
|
|
57
|
+# @brief Default & failover value for plugins path list
|
|
58
|
+PLUGINS_PATH = os.path.join(LodelContext.context_dir(), 'plugins')
|
67
|
59
|
|
68
|
|
-##@brief List storing the mandatory variables expected in a plugin __init__.py
|
69
|
|
-#file
|
|
60
|
+# @brief List storing the mandatory variables expected in a plugin __init__.py
|
|
61
|
+# file
|
70
|
62
|
MANDATORY_VARNAMES = [PLUGIN_NAME_VARNAME, LOADER_FILENAME_VARNAME,
|
71
|
|
- PLUGIN_VERSION_VARNAME]
|
|
63
|
+ PLUGIN_VERSION_VARNAME]
|
72
|
64
|
|
73
|
|
-##@brief Default plugin type
|
74
|
|
-DEFAULT_PLUGIN_TYPE = 'extension' #Value found in lodel/plugin/extensions.py::Extensions._type_conf_name
|
|
65
|
+# @brief Default plugin type
|
|
66
|
+DEFAULT_PLUGIN_TYPE = 'extension' # Value found in lodel/plugin/extensions.py::Extensions._type_conf_name
|
75
|
67
|
|
76
|
|
-## @}
|
|
68
|
+# @}
|
77
|
69
|
|
78
|
|
-##@brief Describe and handle version numbers
|
|
70
|
+# @brief Describe and handle version numbers
|
79
|
71
|
#@ingroup lodel2_plugins
|
80
|
72
|
#
|
81
|
|
-#A version number can be represented by a string like MAJOR.MINOR.PATCH
|
82
|
|
-#or by a list [MAJOR, MINOR,PATCH ].
|
|
73
|
+# A version number can be represented by a string like MAJOR.MINOR.PATCH
|
|
74
|
+# or by a list [MAJOR, MINOR,PATCH ].
|
83
|
75
|
#
|
84
|
|
-#The class implements basics comparison function and string repr
|
|
76
|
+# The class implements basics comparison function and string repr
|
|
77
|
+
|
|
78
|
+
|
85
|
79
|
class PluginVersion(object):
|
86
|
80
|
|
87
|
|
- PROPERTY_LIST = ['major', 'minor', 'revision' ]
|
|
81
|
+ PROPERTY_LIST = ['major', 'minor', 'revision']
|
88
|
82
|
|
89
|
|
- ##@brief Version constructor
|
|
83
|
+ # @brief Version constructor
|
90
|
84
|
#@param *args : You can either give a str that will be splitted on . or you
|
91
|
|
- #can give a iterable containing 3 integer or 3 arguments representing
|
92
|
|
- #major, minor and revision version
|
|
85
|
+ # can give a iterable containing 3 integers or 3 arguments representing
|
|
86
|
+ # major, minor and revision version
|
93
|
87
|
def __init__(self, *args):
|
94
|
|
- self.__version = [0 for _ in range(3) ]
|
|
88
|
+ self.__version = [0 for _ in range(3)]
|
95
|
89
|
if len(args) == 1:
|
96
|
90
|
arg = args[0]
|
97
|
91
|
if isinstance(arg, str):
|
98
|
|
- #Casting from string to version numbers
|
|
92
|
+ # Casting from string to version numbers
|
99
|
93
|
spl = arg.split('.')
|
100
|
94
|
invalid = False
|
101
|
95
|
if len(spl) > 3:
|
102
|
96
|
raise PluginVersionError("The string '%s' is not a valid plugin \
|
103
|
97
|
version number" % arg)
|
104
|
98
|
if len(spl) < 3:
|
105
|
|
- spl += [ 0 for _ in range(3-len(spl))]
|
|
99
|
+ spl += [0 for _ in range(3 - len(spl))]
|
106
|
100
|
try:
|
107
|
101
|
self.__version = [int(s) for s in spl]
|
108
|
102
|
except (ValueError, TypeError):
|
|
@@ -123,29 +117,29 @@ version number" % arg)
|
123
|
117
|
raise PluginError("Expected between 1 and 3 positional arguments \
|
124
|
118
|
but %d arguments found" % len(args))
|
125
|
119
|
else:
|
126
|
|
- for i,v in enumerate(args):
|
|
120
|
+ for i, v in enumerate(args):
|
127
|
121
|
self.__version[i] = int(v)
|
128
|
|
- #Checks that version numbering is correct
|
|
122
|
+ # Checks that version numbering is correct
|
129
|
123
|
for v in self.__version:
|
130
|
124
|
if v < 0:
|
131
|
125
|
raise PluginVersionError("No negative version number allowed !")
|
132
|
126
|
|
133
|
|
- ##@brief Property to access major version number
|
|
127
|
+ # @brief Property to access major version number
|
134
|
128
|
@property
|
135
|
129
|
def major(self):
|
136
|
130
|
return self.__version[0]
|
137
|
131
|
|
138
|
|
- ##@brief Property to access minor version number
|
|
132
|
+ # @brief Property to access minor version number
|
139
|
133
|
@property
|
140
|
134
|
def minor(self):
|
141
|
135
|
return self.__version[1]
|
142
|
136
|
|
143
|
|
- ##@brief Property to access patch version number
|
|
137
|
+ # @brief Property to access patch version number
|
144
|
138
|
@property
|
145
|
139
|
def revision(self):
|
146
|
140
|
return self.__version[2]
|
147
|
141
|
|
148
|
|
- ##@brief Check and prepare comparisoon argument
|
|
142
|
+ # @brief Check and prepare comparison argument
|
149
|
143
|
#@return A PluginVersion instance
|
150
|
144
|
#@throw PluginError if invalid argument provided
|
151
|
145
|
def __cmp_check(self, other):
|
|
@@ -155,10 +149,10 @@ but %d arguments found" % len(args))
|
155
|
149
|
return PluginVersion(other)
|
156
|
150
|
except TypeError:
|
157
|
151
|
raise PluginError("Cannot compare argument '%s' with \
|
158
|
|
-a PluginVerison instance" % other)
|
|
152
|
+a PluginVersion instance" % other)
|
159
|
153
|
return other
|
160
|
154
|
|
161
|
|
- ##@brief Allow accessing to versions parts using interger index
|
|
155
|
+ # @brief Allow accessing to version parts using integer index
|
162
|
156
|
#@param key int : index
|
163
|
157
|
#@return major for key == 0, minor for key == 1, revision for key == 2
|
164
|
158
|
def __getitem__(self, key):
|
|
@@ -208,40 +202,42 @@ a PluginVerison instance" % other)
|
208
|
202
|
return "{'major': %d, 'minor': %d, 'revision': %d}" % tuple(
|
209
|
203
|
self.__version)
|
210
|
204
|
|
211
|
|
-##@brief Plugin metaclass that allows to "catch" child class declaration
|
|
205
|
+# @brief Plugin metaclass that allows to "catch" child class declaration
|
212
|
206
|
#@ingroup lodel2_plugins
|
213
|
207
|
#
|
214
|
|
-#Automatic script registration on child class declaration
|
|
208
|
+# Automatic script registration on child class declaration
|
|
209
|
+
|
|
210
|
+
|
215
|
211
|
class MetaPlugType(type):
|
216
|
212
|
|
217
|
|
- ##@brief Dict storing all plugin types
|
|
213
|
+ # @brief Dict storing all plugin types
|
218
|
214
|
#
|
219
|
|
- #key is the _type_conf_name and value is the class
|
|
215
|
+ # key is the _type_conf_name and value is the class
|
220
|
216
|
_all_ptypes = dict()
|
221
|
217
|
|
222
|
|
- ##@brief type constructor reimplementation
|
|
218
|
+ # @brief type constructor reimplementation
|
223
|
219
|
def __init__(self, name, bases, attrs):
|
224
|
|
- #Here we can store all child classes of Plugin
|
|
220
|
+ # Here we can store all child classes of Plugin
|
225
|
221
|
super().__init__(name, bases, attrs)
|
226
|
222
|
if len(bases) == 1 and bases[0] == object:
|
227
|
223
|
return
|
228
|
|
- #Regitering a new plugin type
|
|
224
|
+ # Regitering a new plugin type
|
229
|
225
|
MetaPlugType._all_ptypes[self._type_conf_name] = self
|
230
|
226
|
|
231
|
|
- ##@brief Accessor to the list of plugin types
|
|
227
|
+ # @brief Accessor to the list of plugin types
|
232
|
228
|
#@return A copy of _all_ptypes attribute (a dict with typename as key
|
233
|
|
- #and the class as value)
|
|
229
|
+ # and the class as value)
|
234
|
230
|
@classmethod
|
235
|
231
|
def all_types(cls):
|
236
|
232
|
return copy.copy(cls._all_ptypes)
|
237
|
233
|
|
238
|
|
- ##@brief Accessor to the list of plugin names
|
239
|
|
- #@return a list of plugin name
|
|
234
|
+ # @brief Accessor to the list of plugin names
|
|
235
|
+ #@return a list of all plugin names
|
240
|
236
|
@classmethod
|
241
|
237
|
def all_ptype_names(cls):
|
242
|
238
|
return list(cls._all_ptypes.keys())
|
243
|
239
|
|
244
|
|
- ##@brief Given a plugin type name return a Plugin child class
|
|
240
|
+ # @brief Given a plugin type name returns a Plugin child class
|
245
|
241
|
#@param ptype_name str : a plugin type name
|
246
|
242
|
#@return A Plugin child class
|
247
|
243
|
#@throw PluginError if ptype_name is not an exsiting plugin type name
|
|
@@ -251,14 +247,14 @@ class MetaPlugType(type):
|
251
|
247
|
raise PluginError("Unknown plugin type '%s'" % ptype_name)
|
252
|
248
|
return cls._all_ptypes[ptype_name]
|
253
|
249
|
|
254
|
|
- ##@brief Call the clear classmethod on each child classes
|
|
250
|
+ # @brief Call the clear classmethod on each child classes
|
255
|
251
|
@classmethod
|
256
|
252
|
def clear_cls(cls):
|
257
|
253
|
for pcls in cls._all_ptypes.values():
|
258
|
254
|
pcls.clear_cls()
|
259
|
255
|
|
260
|
256
|
|
261
|
|
-##@brief Handle plugins
|
|
257
|
+# @brief Handle plugins
|
262
|
258
|
#@ingroup lodel2_plugins
|
263
|
259
|
#
|
264
|
260
|
# An instance represent a loaded plugin. Class methods allow to load/preload
|
|
@@ -270,32 +266,32 @@ class MetaPlugType(type):
|
270
|
266
|
# 3. the loader call load_all to register hooks etc
|
271
|
267
|
class Plugin(object, metaclass=MetaPlugType):
|
272
|
268
|
|
273
|
|
- ##@brief Stores Plugin instances indexed by name
|
|
269
|
+ # @brief Stores Plugin instances indexed by name
|
274
|
270
|
_plugin_instances = dict()
|
275
|
271
|
|
276
|
|
- ##@brief Attribute used by load_all and load methods to detect circular
|
277
|
|
- #dependencies
|
|
272
|
+ # @brief Attribute used by load_all and load methods to detect circular
|
|
273
|
+ # dependencies
|
278
|
274
|
_load_called = []
|
279
|
275
|
|
280
|
|
- ##@brief Attribute that stores plugins list from discover cache file
|
|
276
|
+ # @brief Attribute that stores plugins list from discover cache file
|
281
|
277
|
_plugin_list = None
|
282
|
278
|
|
283
|
279
|
#@brief Designed to store, in child classes, the confspec indicating \
|
284
|
|
- #where plugin list is stored
|
|
280
|
+ # where plugin list is stored
|
285
|
281
|
_plist_confspecs = None
|
286
|
282
|
|
287
|
|
- ##@brief The name of the plugin type in the confguration
|
|
283
|
+ # @brief The name of the plugin type in the confguration
|
288
|
284
|
#
|
289
|
|
- #None in abstract classes and implemented by child classes
|
|
285
|
+ # None in abstract classes and implemented by child classes
|
290
|
286
|
_type_conf_name = None
|
291
|
287
|
|
292
|
|
- ##@brief Stores virtual modules uniq key
|
|
288
|
+ # @brief Stores virtual modules uniq key
|
293
|
289
|
#@note When testing if a dir contains a plugin, if we reimport the __init__
|
294
|
|
- #in a module with the same name, all non existing value (plugin_type for
|
295
|
|
- #example) are replaced by previous plugin values
|
|
290
|
+ # in a module with the same name, all non existing value (plugin_type for
|
|
291
|
+ # example) are replaced by previous plugin values
|
296
|
292
|
_mod_cnt = 0
|
297
|
293
|
|
298
|
|
- ##@brief Plugin class constructor
|
|
294
|
+ # @brief Plugin class constructor
|
299
|
295
|
#
|
300
|
296
|
# Called by setting in early stage of lodel2 boot sequence using classmethod
|
301
|
297
|
# register
|
|
@@ -304,18 +300,18 @@ class Plugin(object, metaclass=MetaPlugType):
|
304
|
300
|
# @throw PluginError
|
305
|
301
|
def __init__(self, plugin_name):
|
306
|
302
|
|
307
|
|
- ##@brief The plugin name
|
|
303
|
+ # @brief The plugin name
|
308
|
304
|
self.name = plugin_name
|
309
|
|
- ##@brief The plugin package path
|
|
305
|
+ # @brief The plugin package path
|
310
|
306
|
self.path = self.plugin_path(plugin_name)
|
311
|
307
|
|
312
|
|
- ##@brief Stores the plugin module
|
|
308
|
+ # @brief Stores the plugin module
|
313
|
309
|
self.module = None
|
314
|
|
- ##@brief Stores the plugin loader module
|
|
310
|
+ # @brief Stores the plugin loader module
|
315
|
311
|
self.__loader_module = None
|
316
|
|
- ##@brief The plugin confspecs
|
|
312
|
+ # @brief The plugin confspecs
|
317
|
313
|
self.__confspecs = dict()
|
318
|
|
- ##@brief Boolean flag telling if the plugin is loaded or not
|
|
314
|
+ # @brief Boolean flag telling if the plugin is loaded or not
|
319
|
315
|
self.loaded = False
|
320
|
316
|
|
321
|
317
|
# Importing __init__.py infos in it
|
|
@@ -333,18 +329,18 @@ class Plugin(object, metaclass=MetaPlugType):
|
333
|
329
|
except AttributeError:
|
334
|
330
|
msg = "Malformed plugin {plugin} . No {varname} not {filevar} found in __init__.py"
|
335
|
331
|
msg = msg.format(
|
336
|
|
- plugin = self.name,
|
337
|
|
- varname = CONFSPEC_VARNAME,
|
338
|
|
- filevar = CONFSPEC_FILENAME_VARNAME)
|
|
332
|
+ plugin=self.name,
|
|
333
|
+ varname=CONFSPEC_VARNAME,
|
|
334
|
+ filevar=CONFSPEC_FILENAME_VARNAME)
|
339
|
335
|
raise PluginError(msg)
|
340
|
336
|
except ImportError as e:
|
341
|
337
|
msg = "Broken plugin {plugin} : {expt}"
|
342
|
338
|
msg = msg.format(
|
343
|
|
- plugin = self.name,
|
344
|
|
- expt = str(e))
|
|
339
|
+ plugin=self.name,
|
|
340
|
+ expt=str(e))
|
345
|
341
|
raise PluginError(msg)
|
346
|
342
|
except Exception as e:
|
347
|
|
- msg = "Plugin '%s' :"+str(e)
|
|
343
|
+ msg = "Plugin '%s' :" + str(e)
|
348
|
344
|
raise e.__class__(msg)
|
349
|
345
|
|
350
|
346
|
try:
|
|
@@ -353,13 +349,13 @@ class Plugin(object, metaclass=MetaPlugType):
|
353
|
349
|
except AttributeError:
|
354
|
350
|
msg = "Broken plugin. {varname} not found in '{filename}'"
|
355
|
351
|
msg = msg.format(
|
356
|
|
- varname = CONFSPEC_VARNAME,
|
357
|
|
- filename = confspec_filename)
|
|
352
|
+ varname=CONFSPEC_VARNAME,
|
|
353
|
+ filename=confspec_filename)
|
358
|
354
|
raise PluginError(msg)
|
359
|
355
|
# loading plugin version
|
360
|
356
|
try:
|
361
|
|
- #this try block should be useless. The existance of
|
362
|
|
- #PLUGIN_VERSION_VARNAME in init file is mandatory
|
|
357
|
+ # this try block should be useless. The existance of
|
|
358
|
+ # PLUGIN_VERSION_VARNAME in init file is mandatory
|
363
|
359
|
self.__version = getattr(self.module, PLUGIN_VERSION_VARNAME)
|
364
|
360
|
except AttributeError:
|
365
|
361
|
msg = "Error that should not append while loading plugin '%s': no \
|
|
@@ -377,8 +373,8 @@ class Plugin(object, metaclass=MetaPlugType):
|
377
|
373
|
raise PluginError("Unknown plugin type '%s'" % self.__type)
|
378
|
374
|
# Load plugin name from init file (just for checking)
|
379
|
375
|
try:
|
380
|
|
- #this try block should be useless. The existance of
|
381
|
|
- #PLUGIN_NAME_VARNAME in init file is mandatory
|
|
376
|
+ # this try block should be useless. The existance of
|
|
377
|
+ # PLUGIN_NAME_VARNAME in init file is mandatory
|
382
|
378
|
pname = getattr(self.module, PLUGIN_NAME_VARNAME)
|
383
|
379
|
except AttributeError:
|
384
|
380
|
msg = "Error that should not append : no %s found in plugin \
|
|
@@ -390,17 +386,17 @@ init file. Malformed plugin"
|
390
|
386
|
name differ from the one found in plugin's init file"
|
391
|
387
|
raise PluginError(msg)
|
392
|
388
|
|
393
|
|
- ##@brief Try to import a file from a variable in __init__.py
|
|
389
|
+ # @brief Try to import a file from a variable in __init__.py
|
394
|
390
|
#@param varname str : The variable name
|
395
|
391
|
#@return loaded module
|
396
|
392
|
#@throw AttributeError if varname not found
|
397
|
393
|
#@throw ImportError if the file fails to be imported
|
398
|
394
|
#@throw PluginError if the filename was not valid
|
399
|
395
|
#@todo Some strange things append :
|
400
|
|
- #when loading modules in test self.module.__name__ does not contains
|
401
|
|
- #the package... but in prod cases the self.module.__name__ is
|
402
|
|
- #the module fullname... Just a reminder note to explain the dirty
|
403
|
|
- #if on self_modname
|
|
396
|
+ # when loading modules in test self.module.__name__ does not contains
|
|
397
|
+ # the package... but in prod cases the self.module.__name__ is
|
|
398
|
+ # the module fullname... Just a reminder note to explain the dirty
|
|
399
|
+ # if on self_modname
|
404
|
400
|
def _import_from_init_var(self, varname):
|
405
|
401
|
# Read varname
|
406
|
402
|
try:
|
|
@@ -408,10 +404,10 @@ name differ from the one found in plugin's init file"
|
408
|
404
|
except AttributeError:
|
409
|
405
|
msg = "Malformed plugin {plugin}. No {varname} found in __init__.py"
|
410
|
406
|
msg = msg.format(
|
411
|
|
- plugin = self.name,
|
412
|
|
- varname = LOADER_FILENAME_VARNAME)
|
|
407
|
+ plugin=self.name,
|
|
408
|
+ varname=LOADER_FILENAME_VARNAME)
|
413
|
409
|
raise PluginError(msg)
|
414
|
|
- #Path are not allowed
|
|
410
|
+ # Path are not allowed
|
415
|
411
|
if filename != os.path.basename(filename):
|
416
|
412
|
msg = "Invalid {varname} content : '{fname}' for plugin {name}"
|
417
|
413
|
msg = msg.format(
|
|
@@ -419,17 +415,17 @@ name differ from the one found in plugin's init file"
|
419
|
415
|
fname=filename,
|
420
|
416
|
name=self.name)
|
421
|
417
|
raise PluginError(msg)
|
422
|
|
- #See the todo
|
|
418
|
+ # See the todo
|
423
|
419
|
if len(self.module.__name__.split('.')) == 1:
|
424
|
420
|
self_modname = self.module.__package__
|
425
|
421
|
else:
|
426
|
422
|
self_modname = self.module.__name__
|
427
|
|
- #extract module name from filename
|
|
423
|
+ # extract module name from filename
|
428
|
424
|
base_mod = '.'.join(filename.split('.')[:-1])
|
429
|
|
- module_name = self_modname+"."+base_mod
|
|
425
|
+ module_name = self_modname + "." + base_mod
|
430
|
426
|
return importlib.import_module(module_name)
|
431
|
427
|
|
432
|
|
- ##@brief Return associated module name
|
|
428
|
+ # @brief Return associated module name
|
433
|
429
|
def module_name(self):
|
434
|
430
|
path_array = self.path.split('/')
|
435
|
431
|
if 'plugins' not in self.path:
|
|
@@ -437,7 +433,7 @@ name differ from the one found in plugin's init file"
|
437
|
433
|
self.name, self.path))
|
438
|
434
|
return '.'.join(['lodel'] + path_array[path_array.index('plugins'):])
|
439
|
435
|
|
440
|
|
- ##@brief Check dependencies of plugin
|
|
436
|
+ # @brief Check dependencies of plugin
|
441
|
437
|
#@return A list of plugin name to be loaded before
|
442
|
438
|
def check_deps(self):
|
443
|
439
|
try:
|
|
@@ -452,16 +448,16 @@ name differ from the one found in plugin's init file"
|
452
|
448
|
except PluginError:
|
453
|
449
|
errors.append(plugin_name)
|
454
|
450
|
if len(errors) > 0:
|
455
|
|
- raise PluginError( "Bad dependencie for '%s' :"%self.name,
|
456
|
|
- ', '.join(errors))
|
|
451
|
+ raise PluginError("Bad dependencie for '%s' :" % self.name,
|
|
452
|
+ ', '.join(errors))
|
457
|
453
|
return result
|
458
|
454
|
|
459
|
|
- ##@brief Check if the plugin should be activated
|
|
455
|
+ # @brief Check if the plugin should be activated
|
460
|
456
|
#
|
461
|
|
- #Try to fetch a function called @ref ACTIVATE_METHOD_NAME in __init__.py
|
462
|
|
- #of a plugin. If none found assert that the plugin can be loaded, else
|
463
|
|
- #the method is called. If it returns anything else that True, the plugin
|
464
|
|
- #is noted as not activable
|
|
457
|
+ # Try to fetch a function called @ref ACTIVATE_METHOD_NAME in __init__.py
|
|
458
|
+ # of a plugin. If none found assert that the plugin can be loaded, else
|
|
459
|
+ # the method is called. If it returns anything else that True, the plugin
|
|
460
|
+ # is noted as not activable
|
465
|
461
|
#
|
466
|
462
|
# @note Maybe we have to exit everything if a plugin cannot be loaded...
|
467
|
463
|
def activable(self):
|
|
@@ -471,35 +467,35 @@ name differ from the one found in plugin's init file"
|
471
|
467
|
msg = "No %s method found for plugin %s. Assuming plugin is ready to be loaded"
|
472
|
468
|
msg %= (ACTIVATE_METHOD_NAME, self.name)
|
473
|
469
|
logger.debug(msg)
|
474
|
|
- test_fun = lambda:True
|
|
470
|
+ test_fun = lambda: True
|
475
|
471
|
return test_fun()
|
476
|
472
|
|
477
|
|
- ##@brief Load a plugin
|
|
473
|
+ # @brief Load a plugin
|
478
|
474
|
#
|
479
|
|
- #Loading a plugin means importing a file. The filename is defined in the
|
480
|
|
- #plugin's __init__.py file in a LOADER_FILENAME_VARNAME variable.
|
|
475
|
+ # Loading a plugin means importing a file. The filename is defined in the
|
|
476
|
+ # plugin's __init__.py file in a LOADER_FILENAME_VARNAME variable.
|
481
|
477
|
#
|
482
|
|
- #The loading process has to take care of other things :
|
|
478
|
+ # The loading process has to take care of other things :
|
483
|
479
|
#- loading dependencies (other plugins)
|
484
|
480
|
#- check that the plugin can be activated using Plugin.activate() method
|
485
|
481
|
#- avoid circular dependencies infinite loop
|
486
|
482
|
def _load(self):
|
487
|
483
|
if self.loaded:
|
488
|
484
|
return
|
489
|
|
- #Test that plugin "wants" to be activated
|
|
485
|
+ # Test that plugin "wants" to be activated
|
490
|
486
|
activable = self.activable()
|
491
|
487
|
if not(activable is True):
|
492
|
488
|
msg = "Plugin %s is not activable : %s"
|
493
|
489
|
msg %= (self.name, activable)
|
494
|
490
|
raise PluginError(msg)
|
495
|
491
|
|
496
|
|
- #Circular dependencie detection
|
|
492
|
+ # Circular dependencie detection
|
497
|
493
|
if self.name in self._load_called:
|
498
|
494
|
raise PluginError("Circular dependencie in Plugin detected. Abording")
|
499
|
495
|
else:
|
500
|
496
|
self._load_called.append(self.name)
|
501
|
497
|
|
502
|
|
- #Dependencie load
|
|
498
|
+ # Dependencie load
|
503
|
499
|
for dependencie in self.check_deps():
|
504
|
500
|
activable = dependencie.activable()
|
505
|
501
|
if activable is True:
|
|
@@ -507,11 +503,11 @@ name differ from the one found in plugin's init file"
|
507
|
503
|
else:
|
508
|
504
|
msg = "Plugin {plugin_name} not activable because it depends on plugin {dep_name} that is not activable : {reason}"
|
509
|
505
|
msg = msg.format(
|
510
|
|
- plugin_name = self.name,
|
511
|
|
- dep_name = dependencie.name,
|
512
|
|
- reason = activable)
|
|
506
|
+ plugin_name=self.name,
|
|
507
|
+ dep_name=dependencie.name,
|
|
508
|
+ reason=activable)
|
513
|
509
|
|
514
|
|
- #Loading the plugin
|
|
510
|
+ # Loading the plugin
|
515
|
511
|
try:
|
516
|
512
|
self.__loader_module = self._import_from_init_var(LOADER_FILENAME_VARNAME)
|
517
|
513
|
except PluginError as e:
|
|
@@ -519,24 +515,24 @@ name differ from the one found in plugin's init file"
|
519
|
515
|
except ImportError as e:
|
520
|
516
|
msg = "Broken plugin {plugin} : {expt}"
|
521
|
517
|
msg = msg.format(
|
522
|
|
- plugin = self.name,
|
523
|
|
- expt = str(e))
|
|
518
|
+ plugin=self.name,
|
|
519
|
+ expt=str(e))
|
524
|
520
|
raise PluginError(msg)
|
525
|
521
|
logger.debug("Plugin '%s' loaded" % self.name)
|
526
|
522
|
self.loaded = True
|
527
|
523
|
|
528
|
|
- ##@brief Returns the loader module
|
|
524
|
+ # @brief Returns the loader module
|
529
|
525
|
#
|
530
|
|
- #Accessor for the __loader__ python module
|
|
526
|
+ # Accessor for the __loader__ python module
|
531
|
527
|
def loader_module(self):
|
532
|
528
|
if not self.loaded:
|
533
|
|
- raise RuntimeError("Plugin %s not loaded yet."%self.name)
|
|
529
|
+ raise RuntimeError("Plugin %s not loaded yet." % self.name)
|
534
|
530
|
return self.__loader_module
|
535
|
531
|
|
536
|
532
|
def __str__(self):
|
537
|
533
|
return "<LodelPlugin '%s' version %s>" % (self.name, self.__version)
|
538
|
534
|
|
539
|
|
- ##@brief Call load method on every pre-loaded plugins
|
|
535
|
+ # @brief Call load method on every pre-loaded plugins
|
540
|
536
|
#
|
541
|
537
|
# Called by loader to trigger hooks registration.
|
542
|
538
|
# This method have to avoid circular dependencies infinite loops. For this
|
|
@@ -554,21 +550,20 @@ name differ from the one found in plugin's init file"
|
554
|
550
|
if len(errors) > 0:
|
555
|
551
|
msg = "Errors while loading plugins :"
|
556
|
552
|
for name, e in errors.items():
|
557
|
|
- msg += "\n\t%20s : %s" % (name,e)
|
|
553
|
+ msg += "\n\t%20s : %s" % (name, e)
|
558
|
554
|
msg += "\n"
|
559
|
555
|
raise PluginError(msg)
|
560
|
556
|
LodelHook.call_hook(
|
561
|
557
|
"lodel2_plugins_loaded", cls, cls._plugin_instances)
|
562
|
558
|
|
563
|
|
-
|
564
|
|
- ##@return a copy of __confspecs attr
|
|
559
|
+ # @return a copy of __confspecs attr
|
565
|
560
|
@property
|
566
|
561
|
def confspecs(self):
|
567
|
562
|
return copy.copy(self.__confspecs)
|
568
|
563
|
|
569
|
|
- ##@brief Accessor to confspec indicating where we can find the plugin list
|
|
564
|
+ # @brief Accessor to confspec indicating where we can find the plugin list
|
570
|
565
|
#@note Abtract method implemented only for Plugin child classes
|
571
|
|
- #This attribute indicate where we fetch the plugin list.
|
|
566
|
+ # This attribute indicate where we fetch the plugin list.
|
572
|
567
|
@classmethod
|
573
|
568
|
def plist_confspecs(cls):
|
574
|
569
|
if cls._plist_confspecs is None:
|
|
@@ -576,11 +571,11 @@ name differ from the one found in plugin's init file"
|
576
|
571
|
%s' % cls.__name__)
|
577
|
572
|
return copy.copy(cls._plist_confspecs)
|
578
|
573
|
|
579
|
|
- ##@brief Retrieves plugin list confspecs
|
|
574
|
+ # @brief Retrieves plugin list confspecs
|
580
|
575
|
#
|
581
|
|
- #This method ask for each Plugin child class the confspecs specifying where
|
582
|
|
- #the wanted plugin list is stored. (For example DatasourcePlugin expect
|
583
|
|
- #that a list of ds plugin to load stored in lodel2 section, datasources key
|
|
576
|
+ # This method ask for each Plugin child class the confspecs specifying where
|
|
577
|
+ # the wanted plugin list is stored. (For example DatasourcePlugin expect
|
|
578
|
+ # that a list of ds plugin to load stored in lodel2 section, datasources key
|
584
|
579
|
# etc...
|
585
|
580
|
@classmethod
|
586
|
581
|
def plugin_list_confspec(cls):
|
|
@@ -592,7 +587,7 @@ name differ from the one found in plugin's init file"
|
592
|
587
|
confspec_append(res, plcs)
|
593
|
588
|
return res
|
594
|
589
|
|
595
|
|
- ##@brief Register a new plugin
|
|
590
|
+ # @brief Register a new plugin
|
596
|
591
|
#
|
597
|
592
|
#@param plugin_name str : The plugin name
|
598
|
593
|
#@return a Plugin instance
|
|
@@ -603,7 +598,7 @@ name differ from the one found in plugin's init file"
|
603
|
598
|
msg = "Plugin allready registered with same name %s"
|
604
|
599
|
msg %= plugin_name
|
605
|
600
|
raise PluginError(msg)
|
606
|
|
- #Here we check that previous discover found a plugin with that name
|
|
601
|
+ # Here we check that previous discover found a plugin with that name
|
607
|
602
|
pdcache = cls.discover()
|
608
|
603
|
if plugin_name not in pdcache:
|
609
|
604
|
raise PluginError("No plugin named '%s' found" % plugin_name)
|
|
@@ -614,7 +609,7 @@ name differ from the one found in plugin's init file"
|
614
|
609
|
logger.debug("Plugin %s available." % plugin)
|
615
|
610
|
return plugin
|
616
|
611
|
|
617
|
|
- ##@brief Plugins instances accessor
|
|
612
|
+ # @brief Plugins instances accessor
|
618
|
613
|
#
|
619
|
614
|
#@param plugin_name str: The plugin name
|
620
|
615
|
#@return a Plugin instance
|
|
@@ -628,7 +623,7 @@ name differ from the one found in plugin's init file"
|
628
|
623
|
msg %= plugin_name
|
629
|
624
|
raise PluginError(msg)
|
630
|
625
|
|
631
|
|
- ##@brief Given a plugin name returns the plugin path
|
|
626
|
+ # @brief Given a plugin name returns the plugin path
|
632
|
627
|
# @param plugin_name str : The plugin name
|
633
|
628
|
# @return the plugin directory path
|
634
|
629
|
@classmethod
|
|
@@ -644,11 +639,11 @@ name differ from the one found in plugin's init file"
|
644
|
639
|
|
645
|
640
|
return plist[plugin_name]['path']
|
646
|
641
|
|
647
|
|
- ##@brief Return the plugin module name
|
|
642
|
+ # @brief Return the plugin module name
|
648
|
643
|
#
|
649
|
|
- #This module name is the "virtual" module where we imported the plugin.
|
|
644
|
+ # This module name is the "virtual" module where we imported the plugin.
|
650
|
645
|
#
|
651
|
|
- #Typically composed like VIRTUAL_PACKAGE_NAME.PLUGIN_NAME
|
|
646
|
+ # Typically composed like VIRTUAL_PACKAGE_NAME.PLUGIN_NAME
|
652
|
647
|
#@warning Brokes subdire feature
|
653
|
648
|
#@param plugin_name str : a plugin name
|
654
|
649
|
#@return a string representing a module name
|
|
@@ -658,7 +653,7 @@ name differ from the one found in plugin's init file"
|
658
|
653
|
def plugin_module_name(cls, plugin_name):
|
659
|
654
|
return "%s.%s" % (VIRTUAL_PACKAGE_NAME, plugin_name)
|
660
|
655
|
|
661
|
|
- ##@brief Start the Plugin class
|
|
656
|
+ # @brief Start the Plugin class
|
662
|
657
|
#
|
663
|
658
|
# Called by Settings.__bootstrap()
|
664
|
659
|
#
|
|
@@ -668,7 +663,7 @@ name differ from the one found in plugin's init file"
|
668
|
663
|
for plugin_name in plugins:
|
669
|
664
|
cls.register(plugin_name)
|
670
|
665
|
|
671
|
|
- ##@brief Attempt to "restart" the Plugin class
|
|
666
|
+ # @brief Attempt to "restart" the Plugin class
|
672
|
667
|
@classmethod
|
673
|
668
|
def clear(cls):
|
674
|
669
|
if cls._plugin_instances != dict():
|
|
@@ -677,15 +672,15 @@ name differ from the one found in plugin's init file"
|
677
|
672
|
cls._load_called = []
|
678
|
673
|
MetaPlugType.clear_cls()
|
679
|
674
|
|
680
|
|
- ##@brief Designed to be implemented by child classes
|
|
675
|
+ # @brief Designed to be implemented by child classes
|
681
|
676
|
@classmethod
|
682
|
677
|
def clear_cls(cls):
|
683
|
678
|
pass
|
684
|
679
|
|
685
|
|
- ##@brief Reccursively walk throught paths to find plugin, then stores
|
686
|
|
- #found plugin in a static var
|
|
680
|
+ # @brief Reccursively walk throught paths to find plugin, then stores
|
|
681
|
+ # found plugin in a static var
|
687
|
682
|
#
|
688
|
|
- #Found plugins are stored in cls._plugin_list
|
|
683
|
+ # Found plugins are stored in cls._plugin_list
|
689
|
684
|
#@note The discover is run only if no cached datas are found
|
690
|
685
|
#@return a list of dict with plugin infos { see @ref _discover }
|
691
|
686
|
#@todo add max_depth and no symlink following feature
|
|
@@ -695,60 +690,60 @@ name differ from the one found in plugin's init file"
|
695
|
690
|
return cls._plugin_list
|
696
|
691
|
logger.info("Running plugin discover")
|
697
|
692
|
tmp_res = cls._discover(PLUGINS_PATH)
|
698
|
|
- #Formating and dedoubloning result
|
|
693
|
+ # Formating and dedoubloning result
|
699
|
694
|
result = dict()
|
700
|
695
|
for pinfos in tmp_res:
|
701
|
696
|
pname = pinfos['name']
|
702
|
|
- if ( pname in result
|
703
|
|
- and pinfos['version'] > result[pname]['version'])\
|
704
|
|
- or pname not in result:
|
|
697
|
+ if (pname in result
|
|
698
|
+ and pinfos['version'] > result[pname]['version'])\
|
|
699
|
+ or pname not in result:
|
705
|
700
|
result[pname] = pinfos
|
706
|
701
|
else:
|
707
|
|
- #dropped
|
|
702
|
+ # dropped
|
708
|
703
|
pass
|
709
|
704
|
cls._plugin_list = result
|
710
|
705
|
return result
|
711
|
706
|
|
712
|
|
- ##@brief Return discover result
|
|
707
|
+ # @brief Return discover result
|
713
|
708
|
#@param refresh bool : if true invalidate all plugin list cache
|
714
|
709
|
#@note If discover cache file not found run discover first
|
715
|
710
|
#@note if refresh is set to True discover MUST have been run at least
|
716
|
|
- #one time. In fact refresh action load the list of path to explore
|
717
|
|
- #from the plugin's discover cache
|
|
711
|
+ # one time. In fact refresh action load the list of path to explore
|
|
712
|
+ # from the plugin's discover cache
|
718
|
713
|
@classmethod
|
719
|
|
- def plugin_list(cls, refresh = False):
|
|
714
|
+ def plugin_list(cls, refresh=False):
|
720
|
715
|
return cls._plugin_list
|
721
|
716
|
|
722
|
|
- ##@brief Return a list of child Class Plugin
|
|
717
|
+ # @brief Return a list of child Class Plugin
|
723
|
718
|
@classmethod
|
724
|
719
|
def plugin_types(cls):
|
725
|
720
|
return MetaPlugType.all_types()
|
726
|
721
|
|
727
|
|
- ##@brief Check if a directory is a plugin module
|
|
722
|
+ # @brief Check if a directory is a plugin module
|
728
|
723
|
#@param path str : path to check
|
729
|
724
|
#@param assert_in_package bool : if False didn't check that path is
|
730
|
|
- #a subdir of PLUGINS_PATH
|
|
725
|
+ # a subdir of PLUGINS_PATH
|
731
|
726
|
#@return a dict with name, version and path if path is a plugin module, else False
|
732
|
727
|
@classmethod
|
733
|
|
- def dir_is_plugin(cls, path, assert_in_package = True):
|
|
728
|
+ def dir_is_plugin(cls, path, assert_in_package=True):
|
734
|
729
|
log_msg = "%s is not a plugin directory because : " % path
|
735
|
730
|
if assert_in_package:
|
736
|
|
- #Check that path is a subdir of PLUGINS_PATH
|
|
731
|
+ # Check that path is a subdir of PLUGINS_PATH
|
737
|
732
|
abspath = os.path.abspath(path)
|
738
|
733
|
if not abspath.startswith(os.path.abspath(PLUGINS_PATH)):
|
739
|
734
|
raise PluginError(
|
740
|
735
|
"%s is not a subdir of %s" % log_msg, PLUGINS_PATH)
|
741
|
|
- #Checks that path exists
|
|
736
|
+ # Checks that path exists
|
742
|
737
|
if not os.path.isdir(path):
|
743
|
738
|
raise ValueError(
|
744
|
739
|
"Expected path to be a directory, but '%s' found" % path)
|
745
|
|
- #Checks that path contains plugin's init file
|
|
740
|
+ # Checks that path contains plugin's init file
|
746
|
741
|
initfile = os.path.join(path, INIT_FILENAME)
|
747
|
742
|
if not os.path.isfile(initfile):
|
748
|
743
|
log_msg += "'%s' not found" % (INIT_FILENAME)
|
749
|
744
|
logger.debug(log_msg)
|
750
|
745
|
return False
|
751
|
|
- #Importing plugin's init file to check contained datas
|
|
746
|
+ # Importing plugin's init file to check contained datas
|
752
|
747
|
try:
|
753
|
748
|
initmod, modname = cls.import_init(path)
|
754
|
749
|
except PluginError as e:
|
|
@@ -756,39 +751,39 @@ name differ from the one found in plugin's init file"
|
756
|
751
|
log_msg %= (INIT_FILENAME, e)
|
757
|
752
|
logger.debug(log_msg)
|
758
|
753
|
return False
|
759
|
|
- #Checking mandatory init module variables
|
|
754
|
+ # Checking mandatory init module variables
|
760
|
755
|
for attr_name in MANDATORY_VARNAMES:
|
761
|
|
- if not hasattr(initmod,attr_name):
|
|
756
|
+ if not hasattr(initmod, attr_name):
|
762
|
757
|
log_msg += " mandatory variable '%s' not found in '%s'"
|
763
|
758
|
log_msg %= (attr_name, INIT_FILENAME)
|
764
|
759
|
logger.debug(log_msg)
|
765
|
760
|
return False
|
766
|
|
- #Fetching plugin's version
|
|
761
|
+ # Fetching plugin's version
|
767
|
762
|
try:
|
768
|
763
|
pversion = getattr(initmod, PLUGIN_VERSION_VARNAME)
|
769
|
764
|
except (NameError, AttributeError) as e:
|
770
|
765
|
msg = "Invalid plugin version found in %s : %s"
|
771
|
766
|
msg %= (path, e)
|
772
|
767
|
raise PluginError(msg)
|
773
|
|
- #Fetching plugin's type
|
|
768
|
+ # Fetching plugin's type
|
774
|
769
|
try:
|
775
|
770
|
ptype = getattr(initmod, PLUGIN_TYPE_VARNAME)
|
776
|
771
|
except (NameError, AttributeError) as e:
|
777
|
772
|
ptype = DEFAULT_PLUGIN_TYPE
|
778
|
773
|
pname = getattr(initmod, PLUGIN_NAME_VARNAME)
|
779
|
774
|
return {'name': pname,
|
780
|
|
- 'version': PluginVersion(pversion),
|
781
|
|
- 'path': path,
|
782
|
|
- 'type': ptype}
|
|
775
|
+ 'version': PluginVersion(pversion),
|
|
776
|
+ 'path': path,
|
|
777
|
+ 'type': ptype}
|
783
|
778
|
|
784
|
|
- ##@brief Import init file from a plugin path
|
|
779
|
+ # @brief Import init file from a plugin path
|
785
|
780
|
#@param path str : Directory path
|
786
|
781
|
#@return a tuple (init_module, module_name)
|
787
|
782
|
#@todo replace by LodelContext usage !!! (not mandatory, this fun
|
788
|
|
- #is only used in plugin discover method)
|
|
783
|
+ # is only used in plugin discover method)
|
789
|
784
|
@classmethod
|
790
|
785
|
def import_init(cls, path):
|
791
|
|
- cls._mod_cnt += 1 # in order to ensure module name unicity
|
|
786
|
+ cls._mod_cnt += 1 # in order to ensure module name unicity
|
792
|
787
|
init_source = os.path.join(path, INIT_FILENAME)
|
793
|
788
|
temp_module = '%s.%s.%s%d' % (
|
794
|
789
|
VIRTUAL_TEMP_PACKAGE_NAME, os.path.basename(os.path.dirname(path)),
|
|
@@ -805,18 +800,18 @@ name differ from the one found in plugin's init file"
|
805
|
800
|
return (res_module, temp_module)
|
806
|
801
|
|
807
|
802
|
@classmethod
|
808
|
|
- def debug_wrapper(cls, updglob = None):
|
|
803
|
+ def debug_wrapper(cls, updglob=None):
|
809
|
804
|
if updglob is not None:
|
810
|
805
|
for k, v in updglob.items():
|
811
|
806
|
globals()[k] = v
|
812
|
807
|
print(logger)
|
813
|
808
|
|
814
|
|
- ##@brief Reccursiv plugin discover given a path
|
|
809
|
+ # @brief Reccursiv plugin discover given a path
|
815
|
810
|
#@param path str : the path to walk through
|
816
|
811
|
#@return A dict with plugin_name as key and {'path':..., 'version':...} as value
|
817
|
812
|
@classmethod
|
818
|
813
|
def _discover(cls, path):
|
819
|
|
- #Ensure plugins symlink creation
|
|
814
|
+ # Ensure plugins symlink creation
|
820
|
815
|
LodelContext.expose_modules(globals(), {
|
821
|
816
|
'lodel.plugins': 'plugins'})
|
822
|
817
|
res = []
|
|
@@ -826,57 +821,60 @@ name differ from the one found in plugin's init file"
|
826
|
821
|
for f in os.listdir(cur_path):
|
827
|
822
|
f_path = os.path.join(cur_path, f)
|
828
|
823
|
if f not in ['.', '..'] and os.path.isdir(f_path):
|
829
|
|
- #Check if it is a plugin directory
|
|
824
|
+ # Check if it is a plugin directory
|
830
|
825
|
test_result = cls.dir_is_plugin(f_path)
|
831
|
826
|
if not (test_result is False):
|
832
|
827
|
logger.info("Plugin '%s' found in %s" % (
|
833
|
|
- test_result['name'],f_path))
|
|
828
|
+ test_result['name'], f_path))
|
834
|
829
|
res.append(test_result)
|
835
|
830
|
else:
|
836
|
831
|
to_explore.append(f_path)
|
837
|
832
|
return res
|
838
|
833
|
|
|
834
|
+
|
839
|
835
|
def debug_wrapper_mod():
|
840
|
|
- print("MOD : ",logger)
|
|
836
|
+ print("MOD : ", logger)
|
841
|
837
|
|
842
|
|
-##@brief Decorator class designed to allow plugins to add custom methods
|
843
|
|
-#to LeObject childs (dyncode objects)
|
|
838
|
+# @brief Decorator class designed to allow plugins to add custom methods
|
|
839
|
+# to LeObject childs (dyncode objects)
|
844
|
840
|
#@ingroup lodel2_plugins
|
845
|
841
|
#
|
|
842
|
+
|
|
843
|
+
|
846
|
844
|
class CustomMethod(object):
|
847
|
|
- ##@brief Stores registered custom methods
|
|
845
|
+ # @brief Stores registered custom methods
|
848
|
846
|
#
|
849
|
|
- #Key = LeObject child class name
|
850
|
|
- #Value = CustomMethod instance
|
|
847
|
+ # Key = LeObject child class name
|
|
848
|
+ # Value = CustomMethod instance
|
851
|
849
|
_custom_methods = dict()
|
852
|
850
|
|
853
|
851
|
INSTANCE_METHOD = 0
|
854
|
852
|
CLASS_METHOD = 1
|
855
|
853
|
STATIC_METHOD = 2
|
856
|
854
|
|
857
|
|
- ##@brief Decorator constructor
|
|
855
|
+ # @brief Decorator constructor
|
858
|
856
|
#@param component_name str : the name of the component to enhance
|
859
|
857
|
#@param method_name str : the name of the method to inject (if None given
|
860
|
858
|
#@param method_type int : take value in one of
|
861
|
|
- #CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or
|
862
|
|
- #CustomMethod::STATIC_METHOD
|
863
|
|
- #use the function name
|
864
|
|
- def __init__(self, component_name, method_name = None, method_type=0):
|
865
|
|
- ##@brief The targeted LeObject child class
|
|
859
|
+ # CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or
|
|
860
|
+ # CustomMethod::STATIC_METHOD
|
|
861
|
+ # use the function name
|
|
862
|
+ def __init__(self, component_name, method_name=None, method_type=0):
|
|
863
|
+ # @brief The targeted LeObject child class
|
866
|
864
|
self._comp_name = component_name
|
867
|
|
- ##@brief The method name
|
|
865
|
+ # @brief The method name
|
868
|
866
|
self._method_name = method_name
|
869
|
|
- ##@brief The function (that will be the injected method)
|
|
867
|
+ # @brief The function (that will be the injected method)
|
870
|
868
|
self._fun = None
|
871
|
|
- ##@brief Stores the type of method (instance, class or static)
|
|
869
|
+ # @brief Stores the type of method (instance, class or static)
|
872
|
870
|
self._type = int(method_type)
|
873
|
|
- if self._type not in (self.INSTANCE_METHOD, self.CLASS_METHOD,\
|
874
|
|
- self.STATIC_METHOD):
|
|
871
|
+ if self._type not in (self.INSTANCE_METHOD, self.CLASS_METHOD,
|
|
872
|
+ self.STATIC_METHOD):
|
875
|
873
|
raise ValueError("Excepted value for method_type was one of \
|
876
|
874
|
CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or \
|
877
|
875
|
CustomMethod::STATIC_METHOD, but got %s" % self._type)
|
878
|
876
|
|
879
|
|
- ##@brief called just after __init__
|
|
877
|
+ # @brief called just after __init__
|
880
|
878
|
#@param fun function : the decorated function
|
881
|
879
|
def __call__(self, fun):
|
882
|
880
|
if self._method_name is None:
|
|
@@ -884,7 +882,7 @@ CustomMethod::STATIC_METHOD, but got %s" % self._type)
|
884
|
882
|
if self._comp_name not in self._custom_methods:
|
885
|
883
|
self._custom_methods[self._comp_name] = list()
|
886
|
884
|
|
887
|
|
- if self._method_name in [ scm._method_name for scm in self._custom_methods[self._comp_name]]:
|
|
885
|
+ if self._method_name in [scm._method_name for scm in self._custom_methods[self._comp_name]]:
|
888
|
886
|
raise RuntimeError("A method named %s allready registered by \
|
889
|
887
|
another plugin : %s" % (
|
890
|
888
|
self._method_name,
|
|
@@ -892,24 +890,24 @@ another plugin : %s" % (
|
892
|
890
|
self._fun = fun
|
893
|
891
|
self._custom_methods[self._comp_name].append(self)
|
894
|
892
|
|
895
|
|
- ##@brief Textual representation
|
|
893
|
+ # @brief Textual representation
|
896
|
894
|
#@return textual representation of the CustomMethod instance
|
897
|
895
|
def __repr__(self):
|
898
|
896
|
res = "<CustomMethod name={method_name} target={classname} \
|
899
|
897
|
source={module_name}.{fun_name}>"
|
900
|
898
|
return res.format(
|
901
|
|
- method_name = self._method_name,
|
902
|
|
- classname = self._comp_name,
|
903
|
|
- module_name = self._fun.__module__,
|
904
|
|
- fun_name = self._fun.__name__)
|
|
899
|
+ method_name=self._method_name,
|
|
900
|
+ classname=self._comp_name,
|
|
901
|
+ module_name=self._fun.__module__,
|
|
902
|
+ fun_name=self._fun.__name__)
|
905
|
903
|
|
906
|
|
- ##@brief Return a well formed method
|
|
904
|
+ # @brief Return a well formed method
|
907
|
905
|
#
|
908
|
906
|
#@note the type of method depends on the _type attribute
|
909
|
907
|
#@return a method directly injectable in the target class
|
910
|
908
|
def __get_method(self):
|
911
|
909
|
if self._type == self.INSTANCE_METHOD:
|
912
|
|
- def custom__get__(self, obj, objtype = None):
|
|
910
|
+ def custom__get__(self, obj, objtype=None):
|
913
|
911
|
return types.MethodType(self, obj, objtype)
|
914
|
912
|
setattr(self._fun, '__get__', custom__get__)
|
915
|
913
|
return self._fun
|
|
@@ -922,18 +920,18 @@ source={module_name}.{fun_name}>"
|
922
|
920
|
CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD \
|
923
|
921
|
CustomMethod::STATIC_METHOD")
|
924
|
922
|
|
925
|
|
- ##@brief Handle custom method dynamic injection in LeAPI dyncode
|
|
923
|
+ # @brief Handle custom method dynamic injection in LeAPI dyncode
|
926
|
924
|
#
|
927
|
|
- #Called by lodel2_dyncode_loaded hook defined at
|
928
|
|
- #lodel.plugin.core_hooks.lodel2_plugin_custom_methods()
|
|
925
|
+ # Called by lodel2_dyncode_loaded hook defined at
|
|
926
|
+ # lodel.plugin.core_hooks.lodel2_plugin_custom_methods()
|
929
|
927
|
#
|
930
|
928
|
#@param cls
|
931
|
929
|
#@param dynclasses LeObject child classes : List of dynamically generated
|
932
|
|
- #LeObject child classes
|
|
930
|
+ # LeObject child classes
|
933
|
931
|
@classmethod
|
934
|
932
|
def set_registered(cls, dynclasses):
|
935
|
933
|
from lodel import logger
|
936
|
|
- dyn_cls_dict = { dc.__name__:dc for dc in dynclasses}
|
|
934
|
+ dyn_cls_dict = {dc.__name__: dc for dc in dynclasses}
|
937
|
935
|
for cls_name, custom_methods in cls._custom_methods.items():
|
938
|
936
|
for custom_method in custom_methods:
|
939
|
937
|
if cls_name not in dyn_cls_dict:
|
|
@@ -950,5 +948,6 @@ with %s" % (custom_method._method_name, custom_method))
|
950
|
948
|
logger.debug(
|
951
|
949
|
"Custom method %s added to target" % custom_method)
|
952
|
950
|
|
|
951
|
+
|
953
|
952
|
def wrapper_debug_fun():
|
954
|
953
|
print(logger)
|