|
@@ -348,6 +348,118 @@ class Plugin(object):
|
348
|
348
|
if cls._load_called != []:
|
349
|
349
|
cls._load_called = []
|
350
|
350
|
|
|
351
|
+##@brief Decorator class designed to allow plugins to add custom methods
|
|
352
|
+#to LeObject childs (dyncode objects)
|
|
353
|
+#
|
|
354
|
+class CustomMethod(object):
|
|
355
|
+ ##@brief Stores registered custom methods
|
|
356
|
+ #
|
|
357
|
+ #Key = LeObject child class name
|
|
358
|
+ #Value = CustomMethod instance
|
|
359
|
+ _custom_methods = dict()
|
|
360
|
+
|
|
361
|
+ INSTANCE_METHOD = 0
|
|
362
|
+ CLASS_METHOD = 1
|
|
363
|
+ STATIC_METHOD = 2
|
|
364
|
+
|
|
365
|
+ ##@brief Decorator constructor
|
|
366
|
+ #@param component_name str : the name of the component to enhance
|
|
367
|
+ #@param method_name str : the name of the method to inject (if None given
|
|
368
|
+ #@param method_type int : take value in one of
|
|
369
|
+ #CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or
|
|
370
|
+ #CustomMethod::STATIC_METHOD
|
|
371
|
+ #use the function name
|
|
372
|
+ def __init__(self, component_name, method_name = None, method_type=0):
|
|
373
|
+ ##@brief The targeted LeObject child class
|
|
374
|
+ self._comp_name = component_name
|
|
375
|
+ ##@brief The method name
|
|
376
|
+ self._method_name = method_name
|
|
377
|
+ ##@brief The function (that will be the injected method)
|
|
378
|
+ self._fun = None
|
|
379
|
+ ##@brief Stores the type of method (instance, class or static)
|
|
380
|
+ self._type = int(method_type)
|
|
381
|
+ if self._type not in (self.INSTANCE_METHOD, self.CLASS_METHOD,\
|
|
382
|
+ self.STATIC_METHOD):
|
|
383
|
+ raise ValueError("Excepted value for method_type was one of \
|
|
384
|
+CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or \
|
|
385
|
+CustomMethod::STATIC_METHOD, but got %s" % self._type)
|
|
386
|
+
|
|
387
|
+ ##@brief called just after __init__
|
|
388
|
+ #@param fun function : the decorated function
|
|
389
|
+ #@param return the function
|
|
390
|
+ def __call__(self, fun):
|
|
391
|
+ if self._method_name is None:
|
|
392
|
+ self._method_name = fun.__name__
|
|
393
|
+ if self._comp_name not in self._custom_methods:
|
|
394
|
+ self._custom_methods[self._comp_name] = list()
|
|
395
|
+
|
|
396
|
+ if self._method_name in [ scm._method_name for scm in self._custom_methods[self._comp_name]]:
|
|
397
|
+ raise RuntimeError("A method named %s allready registered by \
|
|
398
|
+another plugin : %s" % (
|
|
399
|
+ self._method_name,
|
|
400
|
+ self._custom_methods[self._comp_name].__module__))
|
|
401
|
+ self._fun = fun
|
|
402
|
+ self._custom_methods[self._comp_name].append(self)
|
|
403
|
+
|
|
404
|
+ ##@brief Textual representation
|
|
405
|
+ #@return textual representation of the CustomMethod instance
|
|
406
|
+ def __repr__(self):
|
|
407
|
+ res = "<CustomMethod name={method_name} target={classname} \
|
|
408
|
+source={module_name}.{fun_name}>"
|
|
409
|
+ return res.format(
|
|
410
|
+ method_name = self._method_name,
|
|
411
|
+ classname = self._comp_name,
|
|
412
|
+ module_name = self._fun.__module__,
|
|
413
|
+ fun_name = self._fun.__name__)
|
|
414
|
+
|
|
415
|
+ ##@brief Return a well formed method
|
|
416
|
+ #
|
|
417
|
+ #@note the type of method depends on the _type attribute
|
|
418
|
+ #@return a method directly injectable in the target class
|
|
419
|
+ def __get_method(self):
|
|
420
|
+ if self._type == self.INSTANCE_METHOD:
|
|
421
|
+ def custom__get__(self, obj, objtype = None):
|
|
422
|
+ return types.MethodType(self, obj, objtype)
|
|
423
|
+ setattr(self._fun, '__get__', custom__get__)
|
|
424
|
+ return self._fun
|
|
425
|
+ elif self._type == self.CLASS_METHOD:
|
|
426
|
+ return classmethod(self._fun)
|
|
427
|
+ elif self._type == self.STATIC_METHOD:
|
|
428
|
+ return staticmethod(self._fun)
|
|
429
|
+ else:
|
|
430
|
+ raise RuntimeError("Attribute _type is not one of \
|
|
431
|
+CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD \
|
|
432
|
+CustomMethod::STATIC_METHOD")
|
|
433
|
+
|
|
434
|
+ ##@brief Handle custom method dynamic injection in LeAPI dyncode
|
|
435
|
+ #
|
|
436
|
+ #Called by lodel2_dyncode_loaded hook defined at
|
|
437
|
+ #lodel.plugin.core_hooks.lodel2_plugin_custom_methods()
|
|
438
|
+ #
|
|
439
|
+ #@param cls
|
|
440
|
+ #@param dynclasses LeObject child classes : List of dynamically generated
|
|
441
|
+ #LeObject child classes
|
|
442
|
+ @classmethod
|
|
443
|
+ def set_registered(cls, dynclasses):
|
|
444
|
+ from lodel import logger
|
|
445
|
+ dyn_cls_dict = { dc.__name__:dc for dc in dynclasses}
|
|
446
|
+ for cls_name, custom_methods in cls._custom_methods.items():
|
|
447
|
+ for custom_method in custom_methods:
|
|
448
|
+ if cls_name not in dyn_cls_dict:
|
|
449
|
+ logger.error("Custom method %s adding fails : No dynamic \
|
|
450
|
+LeAPI objects named %s." % (custom_method, cls_name))
|
|
451
|
+ elif custom_method._method_name in dir(dyn_cls_dict[cls_name]):
|
|
452
|
+ logger.warning("Overriding existing method '%s' on target \
|
|
453
|
+with %s" % (custom_method._method_name, custom_method))
|
|
454
|
+ else:
|
|
455
|
+ setattr(
|
|
456
|
+ dyn_cls_dict[cls_name],
|
|
457
|
+ custom_method._method_name,
|
|
458
|
+ custom_method.__get_method())
|
|
459
|
+ logger.debug(
|
|
460
|
+ "Custom method %s added to target" % custom_method)
|
|
461
|
+
|
|
462
|
+
|
351
|
463
|
##@page lodel2_plugins Lodel2 plugins system
|
352
|
464
|
#
|
353
|
465
|
# @par Plugin structure
|