1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2025-10-26 02:39:01 +02:00

Add the possibility to add custom methods ton LeAPI dynamic objects using plugin

This commit is contained in:
Yann 2016-06-24 13:46:56 +02:00
commit 36bded3051
5 changed files with 134 additions and 3 deletions

View file

@ -47,6 +47,8 @@ def datasource_init_hook():
def lodel2_dyncode_datasources_init(self, caller, payload):
for cls in dynclasses:
cls._init_datasources()
from lodel.plugin.hooks import LodelHook
LodelHook.call_hook("lodel2_dyncode_loaded", __name__, dynclasses)
"""
##@brief return A list of EmClass sorted by dependencies

View file

@ -40,4 +40,4 @@
# 
from .hooks import LodelHook
from .plugins import Plugin
from .plugins import Plugin, CustomMethod

View file

@ -33,3 +33,8 @@ def datasources_bootstrap_callaback(hook_name, caller, payload):
log_msg = "Found a datasource named '%s' identified by '%s'"
log_msg %= (ds_name, identifier)
logger.debug(log_msg)
@LodelHook("lodel2_dyncode_loaded")
def lodel2_plugins_custom_methods(self, caller, dynclasses):
from lodel.plugin.plugins import CustomMethod
CustomMethod.set_registered(dynclasses)

View file

@ -348,6 +348,118 @@ class Plugin(object):
if cls._load_called != []:
cls._load_called = []
##@brief Decorator class designed to allow plugins to add custom methods
#to LeObject childs (dyncode objects)
#
class CustomMethod(object):
##@brief Stores registered custom methods
#
#Key = LeObject child class name
#Value = CustomMethod instance
_custom_methods = dict()
INSTANCE_METHOD = 0
CLASS_METHOD = 1
STATIC_METHOD = 2
##@brief Decorator constructor
#@param component_name str : the name of the component to enhance
#@param method_name str : the name of the method to inject (if None given
#@param method_type int : take value in one of
#CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or
#CustomMethod::STATIC_METHOD
#use the function name
def __init__(self, component_name, method_name = None, method_type=0):
##@brief The targeted LeObject child class
self._comp_name = component_name
##@brief The method name
self._method_name = method_name
##@brief The function (that will be the injected method)
self._fun = None
##@brief Stores the type of method (instance, class or static)
self._type = int(method_type)
if self._type not in (self.INSTANCE_METHOD, self.CLASS_METHOD,\
self.STATIC_METHOD):
raise ValueError("Excepted value for method_type was one of \
CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD or \
CustomMethod::STATIC_METHOD, but got %s" % self._type)
##@brief called just after __init__
#@param fun function : the decorated function
#@param return the function
def __call__(self, fun):
if self._method_name is None:
self._method_name = fun.__name__
if self._comp_name not in self._custom_methods:
self._custom_methods[self._comp_name] = list()
if self._method_name in [ scm._method_name for scm in self._custom_methods[self._comp_name]]:
raise RuntimeError("A method named %s allready registered by \
another plugin : %s" % (
self._method_name,
self._custom_methods[self._comp_name].__module__))
self._fun = fun
self._custom_methods[self._comp_name].append(self)
##@brief Textual representation
#@return textual representation of the CustomMethod instance
def __repr__(self):
res = "<CustomMethod name={method_name} target={classname} \
source={module_name}.{fun_name}>"
return res.format(
method_name = self._method_name,
classname = self._comp_name,
module_name = self._fun.__module__,
fun_name = self._fun.__name__)
##@brief Return a well formed method
#
#@note the type of method depends on the _type attribute
#@return a method directly injectable in the target class
def __get_method(self):
if self._type == self.INSTANCE_METHOD:
def custom__get__(self, obj, objtype = None):
return types.MethodType(self, obj, objtype)
setattr(self._fun, '__get__', custom__get__)
return self._fun
elif self._type == self.CLASS_METHOD:
return classmethod(self._fun)
elif self._type == self.STATIC_METHOD:
return staticmethod(self._fun)
else:
raise RuntimeError("Attribute _type is not one of \
CustomMethod::INSTANCE_METHOD CustomMethod::CLASS_METHOD \
CustomMethod::STATIC_METHOD")
##@brief Handle custom method dynamic injection in LeAPI dyncode
#
#Called by lodel2_dyncode_loaded hook defined at
#lodel.plugin.core_hooks.lodel2_plugin_custom_methods()
#
#@param cls
#@param dynclasses LeObject child classes : List of dynamically generated
#LeObject child classes
@classmethod
def set_registered(cls, dynclasses):
from lodel import logger
dyn_cls_dict = { dc.__name__:dc for dc in dynclasses}
for cls_name, custom_methods in cls._custom_methods.items():
for custom_method in custom_methods:
if cls_name not in dyn_cls_dict:
logger.error("Custom method %s adding fails : No dynamic \
LeAPI objects named %s." % (custom_method, cls_name))
elif custom_method._method_name in dir(dyn_cls_dict[cls_name]):
logger.warning("Overriding existing method '%s' on target \
with %s" % (custom_method._method_name, custom_method))
else:
setattr(
dyn_cls_dict[cls_name],
custom_method._method_name,
custom_method.__get_method())
logger.debug(
"Custom method %s added to target" % custom_method)
##@page lodel2_plugins Lodel2 plugins system
#
# @par Plugin structure

View file

@ -1,6 +1,6 @@
#-*- coding: utf-8 -*-
from lodel.plugin import LodelHook
from lodel.plugin import LodelHook, CustomMethod
@LodelHook('leapi_get_post')
@LodelHook('leapi_update_pre')
@ -12,4 +12,16 @@ from lodel.plugin import LodelHook
def dummy_callback(hook_name, caller, payload):
if Lodel.settings.Settings.debug:
print("\tHook %s\tcaller %s with %s" % (hook_name, caller, payload))
return payload
return payload
@CustomMethod('Object', 'dummy_method')
def dummy_instance_method(self):
print("Hello world !\
I'm a custom method on an instance of class %s" % self.__class__)
@CustomMethod('Object', 'dummy_class_method', CustomMethod.CLASS_METHOD)
def dummy_instance_method(self):
print("Hello world !\
I'm a custom method on class %s" % self.__class__)