|
@@ -5,9 +5,109 @@ import os, os.path
|
5
|
5
|
import warnings
|
6
|
6
|
|
7
|
7
|
from Lodel.settings import Settings
|
|
8
|
+from Lodel import logger
|
8
|
9
|
from Lodel.hooks import LodelHook
|
9
|
10
|
from Lodel.user import authentication_method, identification_method
|
10
|
11
|
|
|
12
|
+## @brief Handle registration and fetch of plugins functions that will be bound to LeCrud subclass
|
|
13
|
+#
|
|
14
|
+# @note This class exists because of strange behavior with decorator's class inheritance (static attributes was modified by child class)
|
|
15
|
+class LeapiPluginsMethods(object):
|
|
16
|
+
|
|
17
|
+ ## @brief Stores instance methods we want to bind to LeCrud subclass instances
|
|
18
|
+ __methods = {}
|
|
19
|
+ ## @brief Stores classmethods we want to bind to LeCrud subclass
|
|
20
|
+ __classmethods = {}
|
|
21
|
+
|
|
22
|
+ ## @brief Register a new method for LeApi enhancement
|
|
23
|
+ # @param class_method bool : if True register a classmethod
|
|
24
|
+ # @param leapi_classname str : the classname
|
|
25
|
+ # @param method callable : the method to bind
|
|
26
|
+ # @param bind_name str|None : binding name, if None use the decorated function name
|
|
27
|
+ @classmethod
|
|
28
|
+ def register(cls, class_method, leapi_classname, method, bind_name = None):
|
|
29
|
+ if bind_name is None:
|
|
30
|
+ bind_name = method.__name__ # use method name if no bind_name specified
|
|
31
|
+
|
|
32
|
+ methods = cls.__classmethods if class_method else cls.__methods
|
|
33
|
+
|
|
34
|
+ if leapi_classname not in methods:
|
|
35
|
+ methods[leapi_classname] = dict()
|
|
36
|
+ elif bind_name in methods[leapi_classname]:
|
|
37
|
+ orig_fun = methods[leapi_classname][bind_name]
|
|
38
|
+ msg = 'Method overwriting : %s.%s will be registered as %s for %s in place of %s.%s' % (
|
|
39
|
+ inspect.getmodule(orig_fun).__name__,
|
|
40
|
+ orig_fun.__name__,
|
|
41
|
+ bind_name,
|
|
42
|
+ leapi_classname,
|
|
43
|
+ inspect.getmodule(method).__name__,
|
|
44
|
+ method.__name__
|
|
45
|
+ )
|
|
46
|
+ logger.warning(msg)
|
|
47
|
+ methods[leapi_classname][bind_name] = method
|
|
48
|
+ # I was thinking that dict are refrerences...
|
|
49
|
+ if class_method:
|
|
50
|
+ cls.__classmethods = methods
|
|
51
|
+ else:
|
|
52
|
+ cls.__methods = methods
|
|
53
|
+
|
|
54
|
+ ## @brief Fetch all methods that has to be bound to a class
|
|
55
|
+ # @param class_method bool : if true returns classmethods
|
|
56
|
+ # @param call_cls LeCrud subclass : the targeted class
|
|
57
|
+ # @return a dict with wanted bind name as key and callable as value
|
|
58
|
+ @classmethod
|
|
59
|
+ def get_methods(cls, class_method, call_cls):
|
|
60
|
+ methods = cls.__classmethods if class_method else cls.__methods
|
|
61
|
+ result = dict()
|
|
62
|
+ for leapi_classname in methods:
|
|
63
|
+ leapi_cls = call_cls.name2class(leapi_classname)
|
|
64
|
+ # Strange tests are made on call_cls.__name__ because the name2class method
|
|
65
|
+ # is not working when called from LeObject at init time...
|
|
66
|
+ if leapi_cls is False and leapi_classname != call_cls.__name__:
|
|
67
|
+ logger.warning("Unable to find leapi class %s" % (leapi_classname))
|
|
68
|
+ elif leapi_classname == call_cls.__name__ or issubclass(call_cls, leapi_cls):
|
|
69
|
+ result.update(methods[leapi_classname])
|
|
70
|
+ return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+## @brief Decorator class for leapi object enhancement
|
|
74
|
+#
|
|
75
|
+# This decorator allows plugins to bind methods to leapi
|
|
76
|
+# classes.
|
|
77
|
+#
|
|
78
|
+# The decorator take 2 arguments :
|
|
79
|
+# - leapi_cls_name str : the leapi class name
|
|
80
|
+# - method_name str (optional) : the method name once bind (if None the decorated function name will be used)
|
|
81
|
+#
|
|
82
|
+# @note there is another optionnal argument class_method (a bool), but you should not use it and use the leapi_classmethod decorator instead
|
|
83
|
+class leapi_method(object):
|
|
84
|
+ ## @brief Constructor for plugins leapi enhancement methods
|
|
85
|
+ # @param leapi_cls_name str : the classname we want to bind a method to
|
|
86
|
+ # @param method_name str|None : binding name, if None use the decorated function name
|
|
87
|
+ def __init__(self, leapi_cls_name, method_name = None, class_method = False):
|
|
88
|
+ self.__leapi_cls_name = leapi_cls_name
|
|
89
|
+ self.__method_name = method_name
|
|
90
|
+ self.__method = None
|
|
91
|
+ self.__class_method = bool(class_method)
|
|
92
|
+
|
|
93
|
+ ## @brief Called at decoration time
|
|
94
|
+ # @param method callable : the decorated function
|
|
95
|
+ def __call__(self, method):
|
|
96
|
+ self.__method = method
|
|
97
|
+ if self.__method_name is None:
|
|
98
|
+ self.__method_name = method.__name__
|
|
99
|
+ self.__register()
|
|
100
|
+
|
|
101
|
+ ## @biref Register a method to the method we want to bind
|
|
102
|
+ def __register(self):
|
|
103
|
+ LeapiPluginsMethods.register(self.__class_method, self.__leapi_cls_name, self.__method, self.__method_name)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+## @brief Same behavior thant leapi_method but registers classmethods
|
|
107
|
+class leapi_classmethod(leapi_method):
|
|
108
|
+ def __init__(self, leapi_cls_name, method_name = None):
|
|
109
|
+ super().__init__(leapi_cls_name, method_name, True)
|
|
110
|
+
|
11
|
111
|
|
12
|
112
|
## @brief Returns a list of human readable registered hooks
|
13
|
113
|
# @param names list | None : optionnal filter on name
|
|
@@ -40,6 +140,7 @@ def list_hooks(names = None, plugins = None):
|
40
|
140
|
res += "\t- %s.%s ( priority %d )\n" % (module, hook.__name__, priority)
|
41
|
141
|
return res
|
42
|
142
|
|
|
143
|
+
|
43
|
144
|
## @brief Return a human readable list of plugins
|
44
|
145
|
# @param activated bool | None : Optionnal filter on activated or not plugin
|
45
|
146
|
# @return a str
|
|
@@ -82,6 +183,3 @@ def _all_plugins():
|
82
|
183
|
# plugin is a simple python sourcefile
|
83
|
184
|
res.append('%s' % plugin_name)
|
84
|
185
|
return res
|
85
|
|
-
|
86
|
|
-
|
87
|
|
-
|