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,7 +4,8 @@
4 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 10
 ## @brief Main class to handler lodel editorial components (relations and objects)
10 11
 class _LeCrud(object):
@@ -22,6 +23,22 @@ class _LeCrud(object):
22 23
     def __init__(self):
23 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 42
     ## @return A dict with key field name and value a fieldtype instance
26 43
     @classmethod
27 44
     def fieldtypes(cls):

+ 34
- 37
leapi/lefactory.py View File

@@ -2,6 +2,7 @@
2 2
 
3 3
 import importlib
4 4
 import copy
5
+import os.path
5 6
 
6 7
 import EditorialModel
7 8
 from EditorialModel.model import Model
@@ -17,27 +18,11 @@ class LeFactory(object):
17 18
     output_file = 'dyn.py'
18 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 27
     ## @brief Convert an EmType or EmClass name in a python class name
43 28
     # @param name str : The name
@@ -57,13 +42,19 @@ class LeFactory(object):
57 42
             GenericFieldType.module_name(emfield.fieldtype),
58 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 52
     ## @brief Given a Model and an EmClass instances generate python code for corresponding LeClass
62 53
     # @param model Model : A Model instance
63 54
     # @param emclass EmClass : An EmClass instance from model
64 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 58
         cls_fields = dict()
68 59
         cls_linked_types = dict() #keys are LeType classnames and values are tuples (attr_fieldname, attr_fieldtype)
69 60
         #Populating linked_type attr
@@ -74,6 +65,7 @@ class LeFactory(object):
74 65
             ]
75 66
         # Populating fieldtype attr
76 67
         for field in emclass.fields(relational = False):
68
+            self.needed_fieldtypes |= set([field.fieldtype])
77 69
             cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
78 70
             fti = field.fieldtype_instance()
79 71
 
@@ -102,8 +94,7 @@ class LeFactory(object):
102 94
     # @param model Model : A Model instance
103 95
     # @param emtype EmType : An EmType instance from model
104 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 98
         type_fields = list()
108 99
         type_superiors = list()
109 100
         for field in emtype.fields(relational=False):
@@ -128,37 +119,37 @@ class LeFactory(object):
128 119
         )
129 120
 
130 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 123
     # @param datasource_cls Datasource : A datasource class
134 124
     # @param datasource_args dict : A dict representing arguments for datasource_cls instanciation
135 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 131
         result = ""
141 132
         #result += "#-*- coding: utf-8 -*-\n"
142 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 140
 import leapi
146 141
 import leapi.lecrud
147 142
 import leapi.leobject
148 143
 from leapi.leclass import LeClass
149 144
 from leapi.letype import LeType
150
-import EditorialModel.fieldtypes
151
-from EditorialModel.fieldtypes import *
152 145
 """
153 146
 
154 147
         result += """
155 148
 import %s
156
-import %s
157 149
 
158
-""" % (backend_cls.__module__, datasource_cls.__module__)
150
+""" % (datasource_cls.__module__)
159 151
 
160 152
         #Generating the code for LeObject class
161
-        backend_constructor = '%s.%s(**%s)' % (backend_cls.__module__, backend_cls.__name__, repr(backend_args))
162 153
         leobj_me_uid = dict()
163 154
         for comp in model.components('EmType') + model.components('EmClass'):
164 155
             leobj_me_uid[comp.uid] = LeFactory.name2classname(comp.name)
@@ -169,6 +160,7 @@ import %s
169 160
         for fname, ftargs in EditorialModel.classtypes.common_fields.items():
170 161
             ftargs = copy.copy(ftargs)
171 162
             fieldtype = ftargs['fieldtype']
163
+            self.needed_fieldtypes |= set([fieldtype])
172 164
             del(ftargs['fieldtype'])
173 165
 
174 166
             constructor = '{ftname}.EmFieldType(**{ftargs})'.format(
@@ -237,9 +229,9 @@ class {name}(LeType, {leclass}):
237 229
 
238 230
         #Set attributes of created LeClass and LeType child classes
239 231
         for emclass in emclass_l:
240
-            result += LeFactory.emclass_pycode(model, emclass)
232
+            result += self.emclass_pycode(model, emclass)
241 233
         for emtype in emtype_l:
242
-            result += LeFactory.emtype_pycode(model, emtype)
234
+            result += self.emtype_pycode(model, emtype)
243 235
 
244 236
         #Populating LeObject._me_uid dict for a rapid fetch of LeType and LeClass given an EM uid
245 237
         me_uid = {comp.uid: LeFactory.name2classname(comp.name) for comp in emclass_l + emtype_l}
@@ -247,4 +239,9 @@ class {name}(LeType, {leclass}):
247 239
 ## @brief Dict for getting LeClass and LeType child classes given an EM uid
248 240
 LeObject._me_uid = %s""" % "{" + (', '.join(['%s: %s' % (k, v) for k, v in me_uid.items()])) + "}"
249 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 247
         return result

+ 6
- 4
leapi/leobject.py View File

@@ -334,19 +334,21 @@ class _LeObject(object):
334 334
     # @param letype LeType|str|None : LeType child instant or its name
335 335
     # @param leclass LeClass|str|None : LeClass child instant or its name
336 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 340
         if not(leclass is None):
341 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 345
             if not isinstance(leclass, type) or not (leapi.leclass.LeClass in leclass.__bases__) or leclass.__class__ == leapi.leclass.LeClass:
345 346
                 raise ValueError("None | str | LeType child class excpected, but got : '%s' %s"%(leclass,type(leclass)))
346 347
 
347 348
         if not(letype is None):
348 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 353
             if not isinstance(letype, type) or not leapi.letype.LeType in letype.__bases__ or letype.__class__ == leapi.letype.LeType:
352 354
                 raise ValueError("None | str | LeType child class excpected, but got : %s"%type(letype))

+ 2
- 2
leapi/letype.py View File

@@ -170,7 +170,7 @@ class LeType(object):
170 170
     # @throw Leo exception if the lodel_id identify an object from another type
171 171
     @classmethod
172 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 175
     ## @brief Update a LeType in db
176 176
     def db_update(self):
@@ -183,7 +183,7 @@ class LeType(object):
183 183
     # @param cls
184 184
     # return bool
185 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 188
     ## @brief Insert a new LeType in the datasource
189 189
     # @param **datas list : A list of dict containing the datas

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

@@ -13,7 +13,8 @@ from leapi.lefactory import LeFactory
13 13
 class TestLeFactorySyntax(TestCase):
14 14
 
15 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 18
         pyc = compile(py, "dyn.py", 'exec')
18 19
         exec(pyc, globals())
19 20
 
@@ -29,8 +30,7 @@ class TestLeFactory(TestCase):
29 30
         leapi.test.utils.cleanup(cls.tmpdir)
30 31
 
31 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 35
     def test_leobject(self):
36 36
         """ Testing the generated LeObject class """

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

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

+ 16
- 0
refreshdyn.py View File

@@ -0,0 +1,16 @@
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