No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lefactory.py 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. #-*- coding: utf-8 -*-
  2. import importlib
  3. import EditorialModel
  4. from EditorialModel.model import Model
  5. from EditorialModel.fieldtypes.generic import GenericFieldType
  6. ## @brief This class is designed to generated the leobject API given an EditorialModel.model
  7. # @note Contains only static methods
  8. #
  9. # The name is not good but i've no other ideas for the moment
  10. class LeFactory(object):
  11. output_file = 'dyn.py'
  12. modname = None
  13. def __init__(self):
  14. raise NotImplementedError("Not designed (yet?) to be implemented")
  15. ## @brief Return a LeObject child class given its name
  16. # @return a python class or False
  17. @staticmethod
  18. def leobj_from_name(name):
  19. if LeFactory.modname is None:
  20. modname = 'leobject.' + LeFactory.output_file.split('.')[1]
  21. else:
  22. modname = LeFactory.modname
  23. mod = importlib.import_module(modname)
  24. try:
  25. res = getattr(mod, name)
  26. except AttributeError:
  27. return False
  28. return res
  29. @classmethod
  30. def leobject(cls):
  31. return cls.leobj_from_name('LeObject')
  32. ## @brief Convert an EmType or EmClass name in a python class name
  33. # @param name str : The name
  34. # @return name.title()
  35. @staticmethod
  36. def name2classname(name):
  37. if not isinstance(name, str):
  38. raise AttributeError("Argument name should be a str and not a %s" % type(name))
  39. return name.title()
  40. ## @brief Return a call to a FieldType constructor given an EmField
  41. # @param emfield EmField : An EmField
  42. # @return a string representing the python code to instanciate a EmFieldType
  43. @staticmethod
  44. def fieldtype_construct_from_field(emfield):
  45. return '%s.EmFieldType(**%s)' % (
  46. GenericFieldType.module_name(emfield.fieldtype),
  47. repr(emfield._fieldtype_args),
  48. )
  49. ## @brief Given a Model and an EmClass instances generate python code for corresponding LeClass
  50. # @param model Model : A Model instance
  51. # @param emclass EmClass : An EmClass instance from model
  52. # @return A string representing the python code for the corresponding LeClass child class
  53. @staticmethod
  54. def emclass_pycode(model, emclass):
  55. cls_fields = dict()
  56. cls_linked_types = dict() #keys are LeType classnames and values are tuples (attr_fieldname, attr_fieldtype)
  57. #Populating linked_type attr
  58. for rfield in [ f for f in emclass.fields() if f.fieldtype == 'rel2type']:
  59. fti = rfield.fieldtype_instance()
  60. cls_linked_types[LeFactory.name2classname(model.component(fti.rel_to_type_id).name)] = [
  61. (f.name, LeFactory.fieldtype_construct_from_field(f)) for f in model.components('EmField') if f.rel_field_id == rfield.uid
  62. ]
  63. # Populating fieldtype attr
  64. for field in emclass.fields(relational = False):
  65. cls_fields[field.name] = LeFactory.fieldtype_construct_from_field(field)
  66. fti = field.fieldtype_instance()
  67. return """
  68. #Initialisation of {name} class attributes
  69. {name}._fieldtypes = {ftypes}
  70. {name}._linked_types = {ltypes}
  71. {name}._classtype = {classtype}
  72. """.format(
  73. name = LeFactory.name2classname(emclass.name),
  74. ftypes = "{" + (','.join(['\n\t%s:%s' % (repr(f), v) for f, v in cls_fields.items()])) + "\n}",
  75. ltypes = '{'+ (','.join(
  76. [
  77. '\n\t{ltname}:{ltattr_list}'.format(
  78. ltname = lt,
  79. ltattr_list = '['+(','.join([
  80. '(%s,%s)'%(repr(ltname), ltftype) for ltname, ltftype in ltattr
  81. ]))+']'
  82. ) for lt, ltattr in cls_linked_types.items()
  83. ]))+'}',
  84. classtype = repr(emclass.classtype)
  85. )
  86. ## @brief Given a Model and an EmType instances generate python code for corresponding LeType
  87. # @param model Model : A Model instance
  88. # @param emtype EmType : An EmType instance from model
  89. # @return A string representing the python code for the corresponding LeType child class
  90. @staticmethod
  91. def emtype_pycode(model, emtype):
  92. type_fields = list()
  93. type_superiors = list()
  94. for field in emtype.fields(relational=False):
  95. type_fields.append(field.name)
  96. for nat, sup_l in emtype.superiors().items():
  97. type_superiors.append('%s:[%s]' % (
  98. repr(nat),
  99. ','.join([LeFactory.name2classname(sup.name) for sup in sup_l])
  100. ))
  101. return """
  102. #Initialisation of {name} class attributes
  103. {name}._fields = {fields}
  104. {name}._superiors = {dsups}
  105. {name}._leclass = {leclass}
  106. """.format(
  107. name=LeFactory.name2classname(emtype.name),
  108. fields=repr(type_fields),
  109. dsups='{' + (','.join(type_superiors)) + '}',
  110. leclass=LeFactory.name2classname(emtype.em_class.name)
  111. )
  112. ## @brief Generate python code containing the LeObject API
  113. # @param backend_cls Backend : A model backend class
  114. # @param backend_args dict : A dict representing arguments for backend_cls instanciation
  115. # @param datasource_cls Datasource : A datasource class
  116. # @param datasource_args dict : A dict representing arguments for datasource_cls instanciation
  117. # @return A string representing python code
  118. @staticmethod
  119. def generate_python(backend_cls, backend_args, datasource_cls, datasource_args):
  120. model = Model(backend=backend_cls(**backend_args))
  121. result = ""
  122. #result += "#-*- coding: utf-8 -*-\n"
  123. #Putting import directives in result
  124. result += """
  125. from EditorialModel.model import Model
  126. from leobject.leobject import _LeObject
  127. from leobject.leclass import LeClass
  128. from leobject.letype import LeType
  129. import EditorialModel.fieldtypes
  130. """
  131. result += """
  132. import %s
  133. import %s
  134. """ % (backend_cls.__module__, datasource_cls.__module__)
  135. #Generating the code for LeObject class
  136. backend_constructor = '%s.%s(**%s)' % (backend_cls.__module__, backend_cls.__name__, repr(backend_args))
  137. leobj_me_uid = dict()
  138. for comp in model.components('EmType') + model.components('EmClass'):
  139. leobj_me_uid[comp.uid] = LeFactory.name2classname(comp.name)
  140. result += """
  141. ## @brief _LeObject concret clas
  142. # @see leobject::leobject::_LeObject
  143. class LeObject(_LeObject):
  144. _model = Model(backend=%s)
  145. _datasource = %s(**%s)
  146. _me_uid = %s
  147. """ % (backend_constructor, datasource_cls.__module__ + '.' + datasource_cls.__name__, repr(datasource_args), repr(leobj_me_uid))
  148. emclass_l = model.components(EditorialModel.classes.EmClass)
  149. emtype_l = model.components(EditorialModel.types.EmType)
  150. #LeClass child classes definition
  151. for emclass in emclass_l:
  152. result += """
  153. ## @brief EmClass {name} LeClass child class
  154. # @see leobject::leclass::LeClass
  155. class {name}(LeObject,LeClass):
  156. _class_id = {uid}
  157. """.format(
  158. name=LeFactory.name2classname(emclass.name),
  159. uid=emclass.uid
  160. )
  161. #LeType child classes definition
  162. for emtype in emtype_l:
  163. result += """
  164. ## @brief EmType {name} LeType child class
  165. # @see leobject::letype::LeType
  166. class {name}(LeType, {leclass}):
  167. _type_id = {uid}
  168. """.format(
  169. name=LeFactory.name2classname(emtype.name),
  170. leclass=LeFactory.name2classname(emtype.em_class.name),
  171. uid=emtype.uid
  172. )
  173. #Set attributes of created LeClass and LeType child classes
  174. for emclass in emclass_l:
  175. result += LeFactory.emclass_pycode(model, emclass)
  176. for emtype in emtype_l:
  177. result += LeFactory.emtype_pycode(model, emtype)
  178. #Populating LeObject._me_uid dict for a rapid fetch of LeType and LeClass given an EM uid
  179. me_uid = {comp.uid: LeFactory.name2classname(comp.name) for comp in emclass_l + emtype_l}
  180. result += """
  181. ## @brief Dict for getting LeClass and LeType child classes given an EM uid
  182. LeObject._me_uid = %s
  183. """ % "{" + (','.join(['%s:%s' % (k, v) for k, v in me_uid.items()])) + "}"
  184. return result