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 5.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #-*- coding: utf-8 -*-
  2. import functools
  3. from lodel.editorial_model.components import *
  4. from lodel.leapi.leobject import LeObject
  5. from lodel.leapi.datahandlers.base_classes import DataHandler
  6. ##@brief Generate python module code from a given model
  7. # @param model lodel.editorial_model.model.EditorialModel
  8. def dyncode_from_em(model):
  9. # Generation of LeObject child classes code
  10. cls_code, modules, bootstrap_instr = generate_classes(model)
  11. # Header
  12. imports = """from lodel.leapi.leobject import LeObject
  13. from lodel.leapi.datahandlers.base_classes import DataField
  14. from lodel.plugin.hooks import LodelHook
  15. """
  16. for module in modules:
  17. imports += "import %s\n" % module
  18. class_list = [ LeObject.name2objname(cls.uid) for cls in get_classes(model) ]
  19. # formating all components of output
  20. res_code = """#-*- coding: utf-8 -*-
  21. {imports}
  22. {classes}
  23. {bootstrap_instr}
  24. dynclasses = {class_list}
  25. {init_hook}
  26. """.format(
  27. imports = imports,
  28. classes = cls_code,
  29. bootstrap_instr = bootstrap_instr,
  30. class_list = '[' + (', '.join([cls for cls in class_list]))+']',
  31. init_hook = datasource_init_hook(),
  32. )
  33. return res_code
  34. ##@brief Produce the source code of the LodelHook that initialize datasources
  35. #in dyncode
  36. #@return str
  37. def datasource_init_hook():
  38. return """
  39. @LodelHook("lodel2_plugins_loaded")
  40. def lodel2_dyncode_datasources_init(self, caller, payload):
  41. for cls in dynclasses:
  42. cls._init_datasources()
  43. """
  44. ##@brief return A list of EmClass sorted by dependencies
  45. #
  46. # The first elts in the list depends on nothing, etc.
  47. # @return a list of EmClass instances
  48. def emclass_sorted_by_deps(emclass_list):
  49. def emclass_deps_cmp(cls_a, cls_b):
  50. return len(cls_a.parents_recc) - len(cls_b.parents_recc)
  51. ret = sorted(emclass_list, key = functools.cmp_to_key(emclass_deps_cmp))
  52. return ret
  53. ##@brief Returns a list of EmClass that will be represented as LeObject child classes
  54. def get_classes(model):
  55. return [ cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract ]
  56. ##@brief Given an EmField returns the data_handler constructor suitable for dynamic code
  57. def data_handler_constructor(emfield):
  58. #dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
  59. get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
  60. options = []
  61. for name, val in emfield.data_handler_options.items():
  62. if name == 'back_reference' and isinstance(val, tuple):
  63. options.append('{optname}: ({leo_name}, {fieldname})'.format(
  64. optname = repr(name),
  65. leo_name = LeObject.name2objname(val[0]),
  66. fieldname = repr(val[1]),))
  67. else:
  68. options.append(repr(name)+': '+forge_optval(val))
  69. return '{handler_instr}(**{{ {options} }})'.format(
  70. handler_instr = get_handler_class_instr,
  71. options = ', '.join(options))
  72. ##@brief Return a python repr of option values
  73. def forge_optval(optval):
  74. if isinstance(optval, dict):
  75. return '{' + (', '.join( [ '%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
  76. if isinstance(optval, (set, list, tuple)):
  77. return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
  78. if isinstance(optval, EmField):
  79. return "{leobject}.data_handler({fieldname})".format(
  80. leobject = LeObject.name2objname(optval._emclass.uid),
  81. fieldname = repr(optval.uid)
  82. )
  83. elif isinstance(optval, EmClass):
  84. return LeObject.name2objname(optval.uid)
  85. else:
  86. return repr(optval)
  87. ##@brief Generate dyncode from an EmClass
  88. # @param model EditorialModel :
  89. # @param emclass EmClass : EmClass instance
  90. # @return a tuple with emclass python code, a set containing modules name to import, and a list of python instruction to bootstrap dynamic code, in this order
  91. def generate_classes(model):
  92. res = ""
  93. imports = list()
  94. bootstrap = ""
  95. # Generating field list for LeObjects generated from EmClass
  96. for em_class in get_classes(model):
  97. uid = list() # List of fieldnames that are part of the EmClass primary key
  98. parents = list() # List of parents EmClass
  99. # Determine pk
  100. for field in em_class.fields():
  101. if field.data_handler_instance.is_primary_key():
  102. uid.append(field.uid)
  103. # Determine parent for inheritance
  104. if len(em_class.parents) > 0:
  105. for parent in em_class.parents:
  106. parents.append(LeObject.name2objname(parent.uid))
  107. else:
  108. parents.append('LeObject')
  109. datasource_name = em_class.datasource
  110. # Dynamic code generation for LeObject childs classes
  111. em_cls_code = """
  112. class {clsname}({parents}):
  113. _abstract = {abstract}
  114. _fields = None
  115. _uid = {uid_list}
  116. _ro_datasource = None
  117. _rw_datasource = None
  118. _datasource_name = {datasource_name}
  119. """.format(
  120. clsname = LeObject.name2objname(em_class.uid),
  121. parents = ', '.join(parents),
  122. abstract = 'True' if em_class.abstract else 'False',
  123. uid_list = repr(uid),
  124. datasource_name = repr(datasource_name),
  125. )
  126. res += em_cls_code
  127. # Dyncode fields bootstrap instructions
  128. bootstrap += """{classname}._set__fields({fields})
  129. """.format(
  130. classname = LeObject.name2objname(em_class.uid),
  131. fields = '{' + (', '.join(['\n\t%s: %s' % (repr(emfield.uid),data_handler_constructor(emfield)) for emfield in em_class.fields()])) + '}',
  132. )
  133. bootstrap += "\n"
  134. return res, set(imports), bootstrap