1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2025-11-01 20:10:55 +01:00
lodel2_mirror/lodel/leapi/lefactory.py
2017-03-23 15:35:10 +01:00

195 lines
6.8 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#-*- coding: utf-8 -*-
import os
import os.path
import functools
from lodel.context import LodelContext
LodelContext.expose_modules(globals(), {
'lodel.editorial_model.components': ['EmComponent', 'EmClass', 'EmField',
'EmGroup'],
'lodel.leapi.leobject': ['LeObject'],
'lodel.leapi.datahandlers.base_classes': ['DataHandler'],
'lodel.logger': 'logger'})
# @brief Generates python module code from a given model
# @param model lodel.editorial_model.model.EditorialModel
def dyncode_from_em(model):
# Generation of LeObject child classes code
cls_code, bootstrap_instr = generate_classes(model)
# Header
imports = """from lodel.context import LodelContext
LodelContext.expose_modules(globals(), {
'lodel.leapi.leobject': ['LeObject'],
'lodel.leapi.datahandlers.base_classes': ['DataField'],
'lodel.plugin.hooks': ['LodelHook']})
"""
# generates the list of all classes in the editorial model
class_list = [LeObject.name2objname(cls.uid) for cls in get_classes(model)]
# formating all components of output
res_code = """#-*- coding: utf-8 -*-
{imports}
{classes}
{bootstrap_instr}
#List of dynamically generated classes
dynclasses = {class_list}
#Dict of dynamically generated classes indexed by name
dynclasses_dict = {class_dict}
{common_code}
""".format(
imports=imports,
classes=cls_code,
bootstrap_instr=bootstrap_instr,
class_list='[' + (', '.join([cls for cls in class_list])) + ']',
class_dict='{' + (', '.join(["'%s': %s" % (cls, cls)
for cls in class_list])) + '}',
common_code=common_code(),
)
return res_code
# @brief Returns the content of lodel.leapi.lefactory_common
#
# @return a string
def common_code():
res = ""
fname = os.path.dirname(__file__)
fname = os.path.join(fname, 'lefactory_common.py')
with open(fname, 'r') as cfp:
for line in cfp:
if not line.startswith('#-'):
res += line
return res
# @brief return A list of EmClass sorted by dependencies
#
# The first elts in the list depend on nothing, etc.
# @param a list of Emclass instances to be sorted
# @return a list of EmClass instances
def emclass_sorted_by_deps(emclass_list):
def emclass_deps_cmp(cls_a, cls_b):
return len(cls_a.parents_recc) - len(cls_b.parents_recc)
ret = sorted(emclass_list, key=functools.cmp_to_key(emclass_deps_cmp))
return ret
# @brief Returns a list of EmClass instances that will be represented as LeObject child classes
# @param model : an EditorialModel instance
# @return a list of EmClass instances
def get_classes(model):
return [cls for cls in emclass_sorted_by_deps(model.classes()) if not cls.pure_abstract]
# @brief Given an EmField returns the data_handler constructor suitable for dynamic code
# @param a EmField instance
# @return a string
def data_handler_constructor(emfield):
#dh_module_name = DataHandler.module_name(emfield.data_handler_name)+'.DataHandler'
get_handler_class_instr = 'DataField.from_name(%s)' % repr(emfield.data_handler_name)
options = []
for name, val in emfield.data_handler_options.items():
if name == 'back_reference' and isinstance(val, tuple):
options.append('{optname}: ({leo_name}, {fieldname})'.format(
optname=repr(name),
leo_name=LeObject.name2objname(val[0]),
fieldname=repr(val[1]),))
else:
options.append(repr(name) + ': ' + forge_optval(val))
return '{handler_instr}(**{{ {options} }})'.format(
handler_instr=get_handler_class_instr,
options=', '.join(options))
# @brief Return a python repr of option values
# @param A value of any type which represents option
# @return a string
def forge_optval(optval):
if isinstance(optval, dict):
return '{' + (', '.join(['%s: %s' % (repr(name), forge_optval(val)) for name, val in optval.items()])) + '}'
if isinstance(optval, (set, list, tuple)):
return '[' + (', '.join([forge_optval(val) for val in optval])) + ']'
if isinstance(optval, EmField):
return "{leobject}.data_handler({fieldname})".format(
leobject=LeObject.name2objname(optval._emclass.uid),
fieldname=repr(optval.uid)
)
if isinstance(optval, EmClass):
return LeObject.name2objname(optval.uid)
return repr(optval)
# @brief Generate dyncode from an EmClass
# @param model EditorialModel :
# @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
def generate_classes(model):
res = ""
bootstrap = ""
# Generating field list for LeObjects generated from EmClass
for em_class in get_classes(model):
logger.info("Generating a dynamic class for %s" % em_class.uid)
uid = list() # List for fieldnames that are part of the EmClass primary key
parents = list() # List for em_class's parents
# Determines primary key
for field in em_class.fields():
if field.data_handler_instance.is_primary_key():
uid.append(field.uid)
# Determines parentsfor inheritance
if len(em_class.parents) > 0:
for parent in em_class.parents:
parents.append(LeObject.name2objname(parent.uid))
else:
parents.append('LeObject')
datasource_name = em_class.datasource
# Dynamic code generation for LeObject child classes
em_cls_code = """
class {clsname}({parents}):
_abstract = {abstract}
_fields = None
_uid = {uid_list}
_ro_datasource = None
_rw_datasource = None
_datasource_name = {datasource_name}
_child_classes = None
""".format(
clsname=LeObject.name2objname(em_class.uid),
parents=', '.join(parents),
abstract='True' if em_class.abstract else 'False',
uid_list=repr(uid),
datasource_name=repr(datasource_name),
)
res += em_cls_code
# Dyncode fields bootstrap instructions
child_classes = model.get_class_childs(em_class.uid)
if len(child_classes) == 0:
child_classes = 'tuple()'
else:
child_classes = '(%s,)' % (', '.join(
[LeObject.name2objname(emcls.uid) for emcls in child_classes]))
bootstrap += """{classname}._set__fields({fields})
{classname}._child_classes = {child_classes}
""".format(
classname=LeObject.name2objname(em_class.uid),
fields='{' + (', '.join(['\n\t%s: %s' % (repr(emfield.uid),
data_handler_constructor(emfield)) for emfield in em_class.fields()])) + '}',
child_classes=child_classes,
)
bootstrap += "\n"
return res, bootstrap