暂无描述
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

lefactory.py 4.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  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.field_data_handler import FieldDataHandler
  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. cls_code, modules, bootstrap_instr = generate_classes(model)
  10. imports = "from lodel.leapi.leobject import LeObject\n"
  11. for module in modules:
  12. imports += "import %s\n" % module
  13. res_code = """#-*- coding: utf-8 -*-
  14. {imports}
  15. {classes}
  16. {bootstrap_instr}
  17. del(LeObject._set__fields)
  18. """.format(
  19. imports = imports,
  20. classes = cls_code,
  21. bootstrap_instr = bootstrap_instr,
  22. )
  23. return res_code
  24. ## @brief return A list of EmClass sorted by dependencies
  25. #
  26. # The first elts in the list depends on nothing, etc.
  27. # @return a list of EmClass instances
  28. def emclass_sorted_by_deps(emclass_list):
  29. def emclass_deps_cmp(cls_a, cls_b):
  30. if len(cls_a.parents) + len(cls_b.parents) == 0:
  31. return 0
  32. elif len(cls_a.parents) == 0:
  33. return -1
  34. elif len(cls_b.parents) == 0:
  35. return 1
  36. if cls_a in cls_b.parents_recc:
  37. return -1
  38. elif cls_b in cls_a.parents_recc:
  39. return 1
  40. else:
  41. return 0
  42. return sorted(emclass_list, key = functools.cmp_to_key(emclass_deps_cmp))
  43. ## @brief Given an EmField returns the data_handler constructor suitable for dynamic code
  44. def data_handler_constructor(emfield):
  45. dh_module_name = FieldDataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
  46. options = []
  47. #dh_kwargs = '{' + (', '.join(['%s: %s' % (repr(name), forge_optval(val)) for name, val in emfield.data_handler_options.items()])) + '}'
  48. return ('%s(**{' % dh_module_name)+(', '.join([repr(name)+': '+forge_optval(val) for name, val in emfield.data_handler_options.items()])) + '})'
  49. def forge_optval(optval):
  50. if isinstance(optval, dict):
  51. return '{' + (', '.join( [ '%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
  52. if isinstance(optval, (set, list, tuple)):
  53. return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
  54. if isinstance(optval, EmField):
  55. return "{leobject}.data_handler({fieldname})".format(
  56. leobject = LeObject.name2objname(optval._emclass.uid),
  57. fieldname = repr(optval.uid)
  58. )
  59. elif isinstance(optval, EmClass):
  60. return LeObject.name2objname(optval.uid)
  61. else:
  62. return repr(optval)
  63. ## @brief Generate dyncode from an EmClass
  64. # @param model EditorialModel :
  65. # @param emclass EmClass : EmClass instance
  66. # @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
  67. def generate_classes(model):
  68. res = ""
  69. imports = list()
  70. bootstrap = ""
  71. # Generating field list for LeObjects generated from EmClass
  72. for em_class in [ cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract ]:
  73. uid = list() # List of fieldnames that are part of the EmClass primary key
  74. parents = list() # List of parents EmClass
  75. # Determine pk
  76. for field in em_class.fields():
  77. imports.append(FieldDataHandler.module_name(field.data_handler_name))
  78. if field.data_handler_instance.is_primary_key():
  79. uid.append(field.uid)
  80. # Determine parent for inheritance
  81. if len(em_class.parents) > 0:
  82. for parent in em_class.parents:
  83. parents.append(LeObject.name2objname(parent.uid))
  84. else:
  85. parents.append('LeObject')
  86. # Dynamic code generation for LeObject childs classes
  87. em_cls_code = """
  88. class {clsname}({parents}):
  89. __abstract = {abstract}
  90. __fields = None
  91. __uid = {uid_list}
  92. """.format(
  93. clsname = LeObject.name2objname(em_class.uid),
  94. parents = ', '.join(parents),
  95. abstract = 'True' if em_class.abstract else 'False',
  96. uid_list = repr(uid),
  97. )
  98. res += em_cls_code
  99. # Dyncode bootstrap instructions
  100. bootstrap += """{classname}._set__fields({fields})
  101. #del({classname}._set__fields)
  102. """.format(
  103. classname = LeObject.name2objname(em_class.uid),
  104. fields = '{' + (', '.join(['\n\t%s: %s' % (repr(emfield.uid),data_handler_constructor(emfield)) for emfield in em_class.fields()])) + '}',
  105. )
  106. return res, set(imports), bootstrap