Преглед на файлове

Quite dirty & quick commit implementing acl wrapper for dynleapi

Yann Weber преди 8 години
родител
ревизия
729a4ca3eb
променени са 8 файла, в които са добавени 171 реда и са изтрити 3 реда
  1. 1
    0
      Lodel/settings_format.py
  2. 0
    0
      acl/__init__.py
  3. 109
    0
      acl/acl.py
  4. 43
    0
      acl/factory.py
  5. 2
    1
      install/instance_settings.py
  6. 1
    1
      install/loader.py
  7. 5
    1
      install/utils.py
  8. 10
    0
      refreshdyn.py

+ 1
- 0
Lodel/settings_format.py Целия файл

@@ -9,6 +9,7 @@ MANDATORY = [
9 9
     'lodel2_lib_path',
10 10
     'em_file',
11 11
     'dynamic_code_file',
12
+    'acl_dyn_api',
12 13
     'ds_package',
13 14
     'datasource',
14 15
     'mh_classname',

+ 0
- 0
acl/__init__.py Целия файл


+ 109
- 0
acl/acl.py Целия файл

@@ -0,0 +1,109 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import types
4
+import importlib
5
+
6
+## @brief Example implementation of an ACL class
7
+class ACL(object):
8
+    _singleton = None
9
+    
10
+    ## @brief dummy singleton contructor
11
+    def __init__(self, **kwargs):
12
+        ACL._singleton = self
13
+    
14
+    ## @brief dummy singleton getter
15
+    @classmethod
16
+    def instance(cls):
17
+        if cls._singleton is None:
18
+            cls()
19
+        return cls._singleton
20
+
21
+    ## @brief Callback that checks a call to wrapped classes methods
22
+    @classmethod
23
+    def check_call(cls, call_object, method_name, args, kwargs):
24
+        return ACL.instance()._check_call(call_object, method_name, args, kwargs)
25
+
26
+    ## @brief Callback that checks calls to wrapped classes attributes
27
+    @classmethod
28
+    def check_attr(cls, call_object, attr_name):
29
+        return ACL.instance()._check_attr(call_object, attr_name)
30
+
31
+    ## @brief instance version of check_attr()
32
+    # @note this method is the one that fetch and return the attribute
33
+    def _check_attr(self, call_object, attr_name):
34
+        print("Getting attribute '%s' on %s" % (attr_name, call_object))
35
+        return getattr(call_object, attr_name)
36
+
37
+    ## @brief instance version of check_call()
38
+    # @note this method is the one that call the method and return the returned value
39
+    # @param call_object : the object on wich the method is called (class if class methode else instance)
40
+    # @param method_name str : method name
41
+    # @param *args : positional method arguments
42
+    # @param **kwargs : keyword method arguments
43
+    def _check_call(self, call_object, method_name, args, kwargs):
44
+        print("Calling '%s' on %s with %s %s" % (method_name, call_object, args, kwargs))
45
+        #return call_object.__getattribute__(method_name)(*args, **kwargs)
46
+        if method_name == '__init__':
47
+            return call_object(*args, **kwargs)
48
+        return getattr(call_object, method_name)(*args, **kwargs)
49
+
50
+
51
+## @brief A class designed as a wrapper for a method
52
+class AclMethodWrapper(object):
53
+    
54
+    ## @brief Constructor
55
+    # @param wrapped_object PythonClass : A python class
56
+    # @param method_name str : the wrapped method name
57
+    def __init__(self, wrapped_object, method_name):
58
+        self._wrapped_object = wrapped_object
59
+        self._method_name = method_name
60
+
61
+    ## @brief Triggered when the wrapped method is called
62
+    def __call__(self, *args, **kwargs):
63
+        return ACL.check_call(self._wrapped_object, self._method_name, args, kwargs)
64
+
65
+## @brief Return a wrapped class
66
+#
67
+# @param module_name str : LeAPI dyn mopdule name
68
+# @param wrapped_class_name str : wrapped class name
69
+def get_wrapper(module_name, wrapped_class_name):
70
+    
71
+    module = importlib.import_module(module_name)
72
+    wrapped_class = getattr(module, wrapped_class_name)
73
+
74
+    ## @brief Meta class that handles accesses to wrapped class/static attributes
75
+    class MetaAclWrapper(type):
76
+        ## @brief Called when we get a class attribute
77
+        def __getattribute__(cls, name):
78
+            try:
79
+                attr = getattr(wrapped_class, name)
80
+                if isinstance(attr, types.MethodType):
81
+                    return AclMethodWrapper(wrapped_class, name)
82
+                else:
83
+                    return ACL.check_attr(wrapped_class, name)
84
+            except Exception as e:
85
+                #Here we can process the exception or log it
86
+                raise e
87
+
88
+    #class AclWrapper(metaclass=MetaAclWrapper):
89
+    class AclWrapper(object, metaclass=MetaAclWrapper):
90
+
91
+        def __init__(self, *args, **kwargs):
92
+            self._wrapped_instance = ACL.check_call(wrapped_class, '__init__', args, kwargs)
93
+            #self._wrapped_instance = wrapped_class(*args, **kwargs)
94
+
95
+        def __getattribute__(self, name):
96
+            try:
97
+                attr = getattr(
98
+                    super().__getattribute__('_wrapped_instance'),
99
+                    name
100
+                )
101
+                if isinstance(attr, types.MethodType):
102
+                    return AclMethodWrapper(super().__getattribute__('_wrapped_instance'), name)
103
+                else:
104
+                    return ACL.check_attr(super().__getattribute__('_wrapped_instance'), name)
105
+            except Exception as e:
106
+                #do what you want
107
+                raise e
108
+    return AclWrapper
109
+

+ 43
- 0
acl/factory.py Целия файл

@@ -0,0 +1,43 @@
1
+#-*- coding: utf-8 -*-
2
+
3
+import os
4
+
5
+import EditorialModel
6
+from leapi.lecrud import _LeCrud
7
+
8
+class AclFactory(object):
9
+    
10
+    def __init__(self, code_filename = 'acl/dyn.py'):
11
+        self._code_filename = code_filename
12
+        self._dyn_file = os.path.basename(code_filename)
13
+        self._modname = os.path.dirname(code_filename).strip('/').replace('/', '.') #Warning Windaube compatibility
14
+
15
+    ## @brief Create dynamic code python file
16
+    # @param modle Model : An editorial model
17
+    # @param leap_dyn_module_name str : Name of leapi dynamic module name
18
+    def create_pyfile(self, model, leapi_dyn_module_name):
19
+        with open(self._code_filename, "w+") as dynfp:
20
+            dynfp.write(self.generate_python(model, leapi_dyn_module_name))
21
+
22
+    def generate_python(self, model, leapi_module_name):
23
+        result = """## @author acl/factory
24
+
25
+from acl.acl import get_wrapper
26
+"""
27
+        
28
+        classes_to_wrap = ['LeCrud', 'LeObject', 'LeClass', 'LeType', 'LeRelation']
29
+        classes_to_wrap += [ _LeCrud.name2classname(emclass.name) for emclass in model.components(EditorialModel.classes.EmClass) ]
30
+        classes_to_wrap += [ _LeCrud.name2classname(emtype.name) for emtype in model.components(EditorialModel.types.EmType) ]
31
+
32
+        for class_to_wrap in classes_to_wrap:
33
+            result += """
34
+## @brief Wrapped for {class_to_wrap} LeAPI class
35
+# @see {module_name}.{class_to_wrap}
36
+class {class_to_wrap}(get_wrapper('{module_name}', '{class_to_wrap}')):
37
+    pass
38
+
39
+""".format(
40
+            class_to_wrap = class_to_wrap,
41
+            module_name = leapi_module_name
42
+        )
43
+        return result

+ 2
- 1
install/instance_settings.py Целия файл

@@ -11,7 +11,8 @@ templates_base_dir = 'LODEL2_INSTANCE_TEMPLATES_BASE_DIR'
11 11
 debug = False
12 12
 
13 13
 em_file = 'em.json'
14
-dynamic_code_file = 'dynleapi.py'
14
+dynamic_code_file = 'dynleapi.py' #Warning hardcoded module name in utils.py and loader.py
15
+acl_dyn_api = 'api.py' #Warning hardcoded module name in utils.py and loader.py
15 16
 
16 17
 ds_package = 'MySQL'
17 18
 mh_classname = 'MysqlMigrationHandler'

+ 1
- 1
install/loader.py Целия файл

@@ -13,7 +13,7 @@ globals()['Settings'] = Settings
13 13
 
14 14
 # Import dynamic code
15 15
 if os.path.isfile(Settings.dynamic_code_file):
16
-    from dynleapi import *
16
+    from api import *
17 17
 
18 18
 # Import wanted datasource objects
19 19
 for db_modname in ['leapidatasource', 'migrationhandler']:

+ 5
- 1
install/utils.py Целия файл

@@ -18,6 +18,10 @@ def refreshdyn():
18 18
     fact = LeFactory(OUTPUT)
19 19
     # Create the python file
20 20
     fact.create_pyfile(em, LeDataSourceSQL, {})
21
+    # Generate wrapped API
22
+    from acl.factory import AclFactory
23
+    fact = AclFactory(Settings.acl_dyn_api)
24
+    fact.create_pyfile(em, 'dynleapi')
21 25
 
22 26
 
23 27
 def db_init():
@@ -88,4 +92,4 @@ def dir_init():
88 92
             os.unlink(my_file_path)
89 93
         with open(my_file_path, 'w') as my_file:
90 94
             my_file.write(template['content'])
91
-        print("Created %s" % my_file_path)
95
+        print("Created %s" % my_file_path)

+ 10
- 0
refreshdyn.py Целия файл

@@ -7,14 +7,24 @@ from EditorialModel.model import Model
7 7
 from leapi.lefactory import LeFactory
8 8
 from EditorialModel.backend.json_backend import EmBackendJson
9 9
 from DataSource.MySQL.leapidatasource import LeDataSourceSQL
10
+import acl.factory
11
+
12
+if len(sys.argv) > 1:
13
+    raise NotImplementedError("Not implemented (broken because of leapi dyn module name)")
10 14
 
11 15
 OUTPUT = 'leapi/dyn.py' if len(sys.argv) == 1 else sys.argv[1]
12 16
 EMJSON = 'EditorialModel/test/me.json' if len(sys.argv) < 3 else sys.argv[2]
17
+LEAPI_MODNAME = 'leapi.dyn'
18
+
19
+ACL_OUTPUT = 'acl/dyn.py'
13 20
 
14 21
 em = Model(EmBackendJson(EMJSON))
15 22
 
16 23
 fact = LeFactory(OUTPUT)
17 24
 fact.create_pyfile(em, LeDataSourceSQL, {})
18 25
 print(fact.generate_python(em, LeDataSourceSQL, {}))
26
+fact = acl.factory.AclFactory(ACL_OUTPUT)
27
+fact.create_pyfile(em, LEAPI_MODNAME)
28
+print(fact.generate_python(em, LEAPI_MODNAME))
19 29
 
20 30
 

Loading…
Отказ
Запис