Browse Source

Updated LeFactory generated code and LeFactory class itself

Bugfixes in generated code imports
Enhancement on generated classes retrieval
Yann Weber 8 years ago
parent
commit
187886362d
7 changed files with 85 additions and 53 deletions
  1. 18
    1
      leapi/lecrud.py
  2. 34
    37
      leapi/lefactory.py
  3. 6
    4
      leapi/leobject.py
  4. 2
    2
      leapi/letype.py
  5. 3
    3
      leapi/test/test_lefactory.py
  6. 6
    6
      leapi/test/utils.py
  7. 16
    0
      refreshdyn.py

+ 18
- 1
leapi/lecrud.py View File

4
 # @brief This package contains the abstract class representing Lodel Editorial components
4
 # @brief This package contains the abstract class representing Lodel Editorial components
5
 #
5
 #
6
 
6
 
7
-import EditorialModel
7
+import EditorialModel.fieldtypes.generic
8
+import importlib
8
 
9
 
9
 ## @brief Main class to handler lodel editorial components (relations and objects)
10
 ## @brief Main class to handler lodel editorial components (relations and objects)
10
 class _LeCrud(object):
11
 class _LeCrud(object):
22
     def __init__(self):
23
     def __init__(self):
23
         raise NotImplementedError("Abstract class")
24
         raise NotImplementedError("Abstract class")
24
  
25
  
26
+    ## @brief Given a dynamically generated class name return the corresponding python Class
27
+    # @param name str : a concrete class name
28
+    # @return False if no such component
29
+    @classmethod
30
+    def name2class(cls, name):
31
+        #print(dir(cls.__module__))
32
+        mod = importlib.import_module(cls.__module__)
33
+        try:
34
+            return getattr(mod, name)
35
+        except AttributeError:
36
+            return False
37
+
38
+    @classmethod
39
+    def leobject(cls):
40
+        return cls.name2class('LeObject')
41
+
25
     ## @return A dict with key field name and value a fieldtype instance
42
     ## @return A dict with key field name and value a fieldtype instance
26
     @classmethod
43
     @classmethod
27
     def fieldtypes(cls):
44
     def fieldtypes(cls):

+ 34
- 37
leapi/lefactory.py View File

2
 
2
 
3
 import importlib
3
 import importlib
4
 import copy
4
 import copy
5
+import os.path
5
 
6
 
6
 import EditorialModel
7
 import EditorialModel
7
 from EditorialModel.model import Model
8
 from EditorialModel.model import Model
17
     output_file = 'dyn.py'
18
     output_file = 'dyn.py'
18
     modname = None
19
     modname = None
19
 
20
 
20
-    def __init__(self):
21
-        raise NotImplementedError("Not designed (yet?) to be implemented")
22
 
21
 
23
-    ## @brief Return a LeObject child class given its name
24
-    # @return a python class or False
25
-    @staticmethod
26
-    def leobj_from_name(name):
27
-        if LeFactory.modname is None:
28
-            modname = 'leapi.' + LeFactory.output_file.split('.')[0]
29
-        else:
30
-            modname = LeFactory.modname
31
-        mod = importlib.import_module(modname)
32
-        try:
33
-            res = getattr(mod, name)
34
-        except AttributeError:
35
-            return False
36
-        return res
37
-
38
-    @classmethod
39
-    def leobject(cls):
40
-        return cls.leobj_from_name('LeObject')
22
+    def __init__(self, code_filename = 'leapi/dyn.py'):
23
+        self._code_filename = code_filename
24
+        self._dyn_file = os.path.basename(code_filename)
25
+        self._modname = os.path.dirname(code_filename).strip('/').replace('/', '.') #Warning Windaube compatibility
41
 
26
 
42
     ## @brief Convert an EmType or EmClass name in a python class name
27
     ## @brief Convert an EmType or EmClass name in a python class name
43
     # @param name str : The name
28
     # @param name str : The name
57
             GenericFieldType.module_name(emfield.fieldtype),
42
             GenericFieldType.module_name(emfield.fieldtype),
58
             repr(emfield._fieldtype_args),
43
             repr(emfield._fieldtype_args),
59
         )
44
         )
45
+    
46
+    ## @brief Write generated code to a file
47
+    # @todo better options/params for file creation
48
+    def create_pyfile(self, model, datasource_cls, datasource_args):
49
+        with open(self._code_filename, "w+") as dynfp:
50
+            dynfp.write(self.generate_python(model, datasource_cls, datasource_args))
60
 
51
 
61
     ## @brief Given a Model and an EmClass instances generate python code for corresponding LeClass
52
     ## @brief Given a Model and an EmClass instances generate python code for corresponding LeClass
62
     # @param model Model : A Model instance
53
     # @param model Model : A Model instance
63
     # @param emclass EmClass : An EmClass instance from model
54
     # @param emclass EmClass : An EmClass instance from model
64
     # @return A string representing the python code for the corresponding LeClass child class
55
     # @return A string representing the python code for the corresponding LeClass child class
65
-    @staticmethod
66
-    def emclass_pycode(model, emclass):
56
+    def emclass_pycode(self, model, emclass):
57
+
67
         cls_fields = dict()
58
         cls_fields = dict()
68
         cls_linked_types = dict() #keys are LeType classnames and values are tuples (attr_fieldname, attr_fieldtype)
59
         cls_linked_types = dict() #keys are LeType classnames and values are tuples (attr_fieldname, attr_fieldtype)
69
         #Populating linked_type attr
60
         #Populating linked_type attr
74
             ]
65
             ]
75
         # Populating fieldtype attr
66
         # Populating fieldtype attr
76
         for field in emclass.fields(relational = False):
67
         for field in emclass.fields(relational = False):
68
+            self.needed_fieldtypes |= set([field.fieldtype])
77
             cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
69
             cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
78
             fti = field.fieldtype_instance()
70
             fti = field.fieldtype_instance()
79
 
71
 
102
     # @param model Model : A Model instance
94
     # @param model Model : A Model instance
103
     # @param emtype EmType : An EmType instance from model
95
     # @param emtype EmType : An EmType instance from model
104
     # @return A string representing the python code for the corresponding LeType child class
96
     # @return A string representing the python code for the corresponding LeType child class
105
-    @staticmethod
106
-    def emtype_pycode(model, emtype):
97
+    def emtype_pycode(self, model, emtype):
107
         type_fields = list()
98
         type_fields = list()
108
         type_superiors = list()
99
         type_superiors = list()
109
         for field in emtype.fields(relational=False):
100
         for field in emtype.fields(relational=False):
128
         )
119
         )
129
 
120
 
130
     ## @brief Generate python code containing the LeObject API
121
     ## @brief Generate python code containing the LeObject API
131
-    # @param backend_cls Backend : A model backend class
132
-    # @param backend_args dict : A dict representing arguments for backend_cls instanciation
122
+    # @param model EditorialModel.model.Model : An editorial model instance
133
     # @param datasource_cls Datasource : A datasource class
123
     # @param datasource_cls Datasource : A datasource class
134
     # @param datasource_args dict : A dict representing arguments for datasource_cls instanciation
124
     # @param datasource_args dict : A dict representing arguments for datasource_cls instanciation
135
     # @return A string representing python code
125
     # @return A string representing python code
136
-    @staticmethod
137
-    def generate_python(backend_cls, backend_args, datasource_cls, datasource_args):
138
-        model = Model(backend=backend_cls(**backend_args))
126
+    def generate_python(self, model, datasource_cls, datasource_args):
127
+        self.needed_fieldtypes = set() #Stores the list of fieldtypes that will be used by generated code
128
+
129
+        model = model
139
 
130
 
140
         result = ""
131
         result = ""
141
         #result += "#-*- coding: utf-8 -*-\n"
132
         #result += "#-*- coding: utf-8 -*-\n"
142
         #Putting import directives in result
133
         #Putting import directives in result
143
-        result += """## @author LeFactory
134
+        heading = """## @author LeFactory
135
+
136
+import EditorialModel
137
+from EditorialModel import fieldtypes
138
+from EditorialModel.fieldtypes import {needed_fieldtypes_list}
144
 
139
 
145
 import leapi
140
 import leapi
146
 import leapi.lecrud
141
 import leapi.lecrud
147
 import leapi.leobject
142
 import leapi.leobject
148
 from leapi.leclass import LeClass
143
 from leapi.leclass import LeClass
149
 from leapi.letype import LeType
144
 from leapi.letype import LeType
150
-import EditorialModel.fieldtypes
151
-from EditorialModel.fieldtypes import *
152
 """
145
 """
153
 
146
 
154
         result += """
147
         result += """
155
 import %s
148
 import %s
156
-import %s
157
 
149
 
158
-""" % (backend_cls.__module__, datasource_cls.__module__)
150
+""" % (datasource_cls.__module__)
159
 
151
 
160
         #Generating the code for LeObject class
152
         #Generating the code for LeObject class
161
-        backend_constructor = '%s.%s(**%s)' % (backend_cls.__module__, backend_cls.__name__, repr(backend_args))
162
         leobj_me_uid = dict()
153
         leobj_me_uid = dict()
163
         for comp in model.components('EmType') + model.components('EmClass'):
154
         for comp in model.components('EmType') + model.components('EmClass'):
164
             leobj_me_uid[comp.uid] = LeFactory.name2classname(comp.name)
155
             leobj_me_uid[comp.uid] = LeFactory.name2classname(comp.name)
169
         for fname, ftargs in EditorialModel.classtypes.common_fields.items():
160
         for fname, ftargs in EditorialModel.classtypes.common_fields.items():
170
             ftargs = copy.copy(ftargs)
161
             ftargs = copy.copy(ftargs)
171
             fieldtype = ftargs['fieldtype']
162
             fieldtype = ftargs['fieldtype']
163
+            self.needed_fieldtypes |= set([fieldtype])
172
             del(ftargs['fieldtype'])
164
             del(ftargs['fieldtype'])
173
 
165
 
174
             constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
166
             constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
237
 
229
 
238
         #Set attributes of created LeClass and LeType child classes
230
         #Set attributes of created LeClass and LeType child classes
239
         for emclass in emclass_l:
231
         for emclass in emclass_l:
240
-            result += LeFactory.emclass_pycode(model, emclass)
232
+            result += self.emclass_pycode(model, emclass)
241
         for emtype in emtype_l:
233
         for emtype in emtype_l:
242
-            result += LeFactory.emtype_pycode(model, emtype)
234
+            result += self.emtype_pycode(model, emtype)
243
 
235
 
244
         #Populating LeObject._me_uid dict for a rapid fetch of LeType and LeClass given an EM uid
236
         #Populating LeObject._me_uid dict for a rapid fetch of LeType and LeClass given an EM uid
245
         me_uid = {comp.uid: LeFactory.name2classname(comp.name) for comp in emclass_l + emtype_l}
237
         me_uid = {comp.uid: LeFactory.name2classname(comp.name) for comp in emclass_l + emtype_l}
247
 ## @brief Dict for getting LeClass and LeType child classes given an EM uid
239
 ## @brief Dict for getting LeClass and LeType child classes given an EM uid
248
 LeObject._me_uid = %s""" % "{" + (', '.join(['%s: %s' % (k, v) for k, v in me_uid.items()])) + "}"
240
 LeObject._me_uid = %s""" % "{" + (', '.join(['%s: %s' % (k, v) for k, v in me_uid.items()])) + "}"
249
         result += "\n"
241
         result += "\n"
242
+        
243
+        heading = heading.format(needed_fieldtypes_list = ', '.join(self.needed_fieldtypes))
244
+        result = heading + result
245
+
246
+        del(self.needed_fieldtypes)
250
         return result
247
         return result

+ 6
- 4
leapi/leobject.py View File

334
     # @param letype LeType|str|None : LeType child instant or its name
334
     # @param letype LeType|str|None : LeType child instant or its name
335
     # @param leclass LeClass|str|None : LeClass child instant or its name
335
     # @param leclass LeClass|str|None : LeClass child instant or its name
336
     # @return a tuple with 2 python classes (LeTypeChild, LeClassChild)
336
     # @return a tuple with 2 python classes (LeTypeChild, LeClassChild)
337
-    @staticmethod
338
-    def _prepare_targets(letype = None , leclass = None):
337
+    @classmethod
338
+    def _prepare_targets(cls, letype = None , leclass = None):
339
 
339
 
340
         if not(leclass is None):
340
         if not(leclass is None):
341
             if isinstance(leclass, str):
341
             if isinstance(leclass, str):
342
-                leclass = LeFactory.leobj_from_name(leclass)
342
+                leclass = cls.name2class(leclass)
343
+                #leclass = LeFactory.leobj_from_name(leclass)
343
             
344
             
344
             if not isinstance(leclass, type) or not (leapi.leclass.LeClass in leclass.__bases__) or leclass.__class__ == leapi.leclass.LeClass:
345
             if not isinstance(leclass, type) or not (leapi.leclass.LeClass in leclass.__bases__) or leclass.__class__ == leapi.leclass.LeClass:
345
                 raise ValueError("None | str | LeType child class excpected, but got : '%s' %s"%(leclass,type(leclass)))
346
                 raise ValueError("None | str | LeType child class excpected, but got : '%s' %s"%(leclass,type(leclass)))
346
 
347
 
347
         if not(letype is None):
348
         if not(letype is None):
348
             if isinstance(letype, str):
349
             if isinstance(letype, str):
349
-                letype = LeFactory.leobj_from_name(letype)
350
+                letype = cls.name2class(letype)
351
+                #letype = LeFactory.leobj_from_name(letype)
350
 
352
 
351
             if not isinstance(letype, type) or not leapi.letype.LeType in letype.__bases__ or letype.__class__ == leapi.letype.LeType:
353
             if not isinstance(letype, type) or not leapi.letype.LeType in letype.__bases__ or letype.__class__ == leapi.letype.LeType:
352
                 raise ValueError("None | str | LeType child class excpected, but got : %s"%type(letype))
354
                 raise ValueError("None | str | LeType child class excpected, but got : %s"%type(letype))

+ 2
- 2
leapi/letype.py View File

170
     # @throw Leo exception if the lodel_id identify an object from another type
170
     # @throw Leo exception if the lodel_id identify an object from another type
171
     @classmethod
171
     @classmethod
172
     def delete(cls, filters):
172
     def delete(cls, filters):
173
-        return leapi.lefactory.LeFactory.leobject().delete(cls, filters)
173
+        return cls.name2class('LeObject').delete(cls, filters)
174
         
174
         
175
     ## @brief Update a LeType in db
175
     ## @brief Update a LeType in db
176
     def db_update(self):
176
     def db_update(self):
183
     # @param cls
183
     # @param cls
184
     # return bool
184
     # return bool
185
     def update(cls, filters, datas):
185
     def update(cls, filters, datas):
186
-        return leapi.lefactory.LeFactory.leobject().update(letype = cls, filters = filters, datas = datas)
186
+        return cls.leobject().update(letype = cls, filters = filters, datas = datas)
187
         
187
         
188
     ## @brief Insert a new LeType in the datasource
188
     ## @brief Insert a new LeType in the datasource
189
     # @param **datas list : A list of dict containing the datas
189
     # @param **datas list : A list of dict containing the datas

+ 3
- 3
leapi/test/test_lefactory.py View File

13
 class TestLeFactorySyntax(TestCase):
13
 class TestLeFactorySyntax(TestCase):
14
 
14
 
15
     def test_generated_code_syntax(self):
15
     def test_generated_code_syntax(self):
16
-        py = LeFactory.generate_python(**leapi.test.utils.genepy_args)
16
+        fact = LeFactory('leapi/dyn.py')
17
+        py = fact.generate_python(**leapi.test.utils.genepy_args)
17
         pyc = compile(py, "dyn.py", 'exec')
18
         pyc = compile(py, "dyn.py", 'exec')
18
         exec(pyc, globals())
19
         exec(pyc, globals())
19
 
20
 
29
         leapi.test.utils.cleanup(cls.tmpdir)
30
         leapi.test.utils.cleanup(cls.tmpdir)
30
 
31
 
31
     def setUp(self):
32
     def setUp(self):
32
-        backend=leapi.test.utils.genepy_args['backend_cls'](**leapi.test.utils.genepy_args['backend_args'])
33
-        self.model = EditorialModel.model.Model(backend = backend)
33
+        self.model = leapi.test.utils.genepy_args['model']
34
 
34
 
35
     def test_leobject(self):
35
     def test_leobject(self):
36
         """ Testing the generated LeObject class """
36
         """ Testing the generated LeObject class """

+ 6
- 6
leapi/test/utils.py View File

2
 import shutil
2
 import shutil
3
 import sys
3
 import sys
4
 
4
 
5
-import EditorialModel
5
+from EditorialModel.model import Model
6
 import leapi
6
 import leapi
7
 from EditorialModel.backend.json_backend import EmBackendJson
7
 from EditorialModel.backend.json_backend import EmBackendJson
8
 from leapi.datasources.dummy import DummyDatasource
8
 from leapi.datasources.dummy import DummyDatasource
11
 
11
 
12
 
12
 
13
 genepy_args = {
13
 genepy_args = {
14
-    'backend_cls': EmBackendJson,
15
-    'backend_args': {'json_file': 'EditorialModel/test/me.json'},
14
+    'model' : Model(EmBackendJson(json_file = 'EditorialModel/test/me.json')),
16
     'datasource_cls': DummyDatasource,
15
     'datasource_cls': DummyDatasource,
17
     'datasource_args': {}
16
     'datasource_args': {}
18
 }
17
 }
20
 def tmp_load_factory_code(name='dyncode'):
19
 def tmp_load_factory_code(name='dyncode'):
21
     tmpdir = tempfile.mkdtemp('_lodel2_test_dyncode')
20
     tmpdir = tempfile.mkdtemp('_lodel2_test_dyncode')
22
     fname = tmpdir+'/%s.py'%name
21
     fname = tmpdir+'/%s.py'%name
23
-    with open(fname, 'w+') as dynfp:
24
-        dynfp.write(LeFactory.generate_python(**genepy_args))
22
+    
25
     sys.path.append(tmpdir)
23
     sys.path.append(tmpdir)
26
-    LeFactory.modname = name
24
+    fact = LeFactory(fname)
25
+    fact.create_pyfile(**genepy_args)
26
+
27
     return tmpdir
27
     return tmpdir
28
 
28
 
29
 
29
 

+ 16
- 0
refreshdyn.py View File

1
+# -*- coding: utf-8 -*-
2
+
3
+from EditorialModel.model import Model
4
+from leapi.lefactory import LeFactory
5
+from EditorialModel.backend.json_backend import EmBackendJson
6
+from leapi.datasources.ledatasourcesql import LeDataSourceSQL
7
+
8
+OUTPUT = 'leapi/dyn.py'
9
+
10
+em = Model(EmBackendJson('EditorialModel/test/me.json'))
11
+
12
+fact = LeFactory('leapi/dyn.py')
13
+fact.create_pyfile(em, LeDataSourceSQL, {})
14
+print(fact.generate_python(em, LeDataSourceSQL, {}))
15
+
16
+

Loading…
Cancel
Save