Browse Source

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

Yann Weber 8 years ago
parent
commit
36bded3051
5 changed files with 134 additions and 3 deletions
  1. 2
    0
      lodel/leapi/lefactory.py
  2. 1
    1
      lodel/plugin/__init__.py
  3. 5
    0
      lodel/plugin/core_hooks.py
  4. 112
    0
      lodel/plugin/plugins.py
  5. 14
    2
      plugins/dummy/main.py

+ 2
- 0
lodel/leapi/lefactory.py View File

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

+ 1
- 1
lodel/plugin/__init__.py View File

@@ -40,4 +40,4 @@
40 40
41 41
 
42 42
 from .hooks import LodelHook
43
-from .plugins import Plugin
43
+from .plugins import Plugin, CustomMethod

+ 5
- 0
lodel/plugin/core_hooks.py View File

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

+ 112
- 0
lodel/plugin/plugins.py View File

@@ -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

+ 14
- 2
plugins/dummy/main.py View File

@@ -1,6 +1,6 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
-from lodel.plugin import LodelHook
3
+from lodel.plugin import LodelHook, CustomMethod
4 4
 
5 5
 @LodelHook('leapi_get_post')
6 6
 @LodelHook('leapi_update_pre')
@@ -12,4 +12,16 @@ from lodel.plugin import LodelHook
12 12
 def dummy_callback(hook_name, caller, payload):
13 13
     if Lodel.settings.Settings.debug:
14 14
         print("\tHook %s\tcaller %s with %s" % (hook_name, caller, payload))
15
-    return payload   
15
+    return payload
16
+
17
+
18
+@CustomMethod('Object', 'dummy_method')
19
+def dummy_instance_method(self):
20
+    print("Hello world !\
21
+I'm a custom method on an instance of class %s" % self.__class__)
22
+
23
+
24
+@CustomMethod('Object', 'dummy_class_method', CustomMethod.CLASS_METHOD)
25
+def dummy_instance_method(self):
26
+    print("Hello world !\
27
+I'm a custom method on class %s" % self.__class__)

Loading…
Cancel
Save