# -*- coding: iso-8859-1 -*-
#-----------------------------------------------------------------------------
# Modeling Framework: an Object-Relational Bridge for python
#
# Copyright (c) 2001-2004 Sbastien Bigaret <sbigaret@users.sourceforge.net>
# All rights reserved.
#
# This file is part of the Modeling Framework.
#
# This code is distributed under a "3-clause BSD"-style license;
# see the LICENSE file for details.
#-----------------------------------------------------------------------------
"""
PyModelMason generates the python package, modules and classes described
in a model, ready to be used w/ the modeling framework.
Its templates are located in sub-package 'Python_bricks'. The generated code
is compatible with python v2.1 and v2.2.
Given a model, it can generate the appropriate python code in two different
ways: the flat, or 'compact' scheme, and the 'base' scheme.
The former one generates all files within a single directory (namely: in
package model.packageName()), none of which (except the models) can be
overwritten when the code is regenerated.
The so-called 'base' scheme adds a subpackage 'MDL' within the generated
package. All files within MDL/ are ALWAYS overwritten when the python code is
regenerated, while others (in the root package) are never overwritten if they
exist. This is probably the one you want to use if your model changes often.
CVS information
$Id: PyModelMason.py 974 2006-02-26 14:06:07Z sbigaret $
"""
__version__='$Revision: 974 $'[11:-2]
import time, os, string, sys
from ModelMason import ModelMason
from Cheetah.Template import Template
from Python_bricks import init,init_base,model,base_module,module_base,module_compact,setup_tmpl
class InvalidModelError(Exception):
"Raised by checkModelIsValid()"
pass
def checkModelIsValid(model, generation_scheme):
"""
Checks that the supplied model is valid for a given python-code generation
scheme:
- if 'generation_scheme' is 'compact', all models are valid,
- if 'generation_scheme' is 'base', the model is valid iff a class/entity does
not share the same module than any of its subclasses
Parameters:
model -- a instance of Modeling.Model.Model
generation_scheme -- either 'compact' or 'base'
Returns: None
Raises InvalidModelError if the model is invalid.
Note: this is NOT validation of the model itself (responsability of
Modeling.ModelValidation)
"""
if generation_scheme=='compact': return
if generation_scheme=='base':
# Build ordered list of entities
oe=[]
entities=list(model.entities())
entities_processed=[]
for entity in [e for e in entities if not e.parentEntity()]:
oe.append(entity)
entities_processed.append(entity.name())
entities.remove(entity)
while entities:
es=[e for e in entities if e.parentEntity().name() in entities_processed]
for entity in es:
entities.remove(entity)
entities_processed.append(entity.name())
oe.append(entity)
# check every parent against it subentities
for e in oe:
parent_moduleName=e.moduleName()
subs=e.allSubEntities()
for sub in subs:
if sub.moduleName()==parent_moduleName:
raise InvalidModelError, "Model is invalid wrt the 'base' generation scheme: entity '%s' and its subentity '%s' share the same moduleName.\nEither correct this or use the 'compact' generation scheme"%(e.name(), sub.name())
else:
raise ValueError, "Invalid value for 'generation_scheme' (%s), should be either 'base' or 'compact'"%use_scheme
class PyModelMason(ModelMason):
"See the module's documentation for details"
def __init__(self, model, pymodel_path, rootPath=None, verbose_mode=0,
generation_scheme='compact', fake_mode=0):
"""
Initializes the ModelMason so that the built files are based on the
supplied model.
:Parameters:
- `model`: see ModelMason.__init__()
- `pymodel_path`:
- `rootPath`: see ModelMason.__init__()
- `verbode_mode`: see ModelMason.__init__()
- `generation_scheme`: either string ``"compact"`` or ``"base"``
- `fake_mode`: see ModelMason.__init__()
"""
import Modeling
if generation_scheme not in ('compact', 'base'):
raise ValueError,"Parameter generation_scheme can be either 'compact' or 'base'"
checkModelIsValid(model, generation_scheme)
ModelMason.__init__(self, model, rootPath,
Modeling.ModelMasons.PyModelMason, 'Python_bricks',
verbose_mode, fake_mode)
self._entities=[] # used during build
self.generation_scheme=generation_scheme
self.base_dir=generation_scheme=='base' and 'MDL' or ''
self.pymodel_path=pymodel_path
def tmpl_namespace(self):
"""
Namespace for templates: contains keys 'myself', 'model' and 'entities'
NB: 'entities' is set during build(), when generating modules.
"""
nameSpace = {'myself': self,
'model' : self.model,
'entities': self._entities,
'base_dir': self.base_dir,
'entitiesSet': self.entitiesSet,
}
return nameSpace
def build(self):
"-"
#-----------------------------------------------------------------
def create_xmlmodel_files(self, xml_path, py_path):
"Creates or overwrite the model (.xml and .py)"
# model_<modelName>.xml
if os.path.exists(xml_path):
self.log('Overwriting %s'%xml_path)
else:
self.log('Generating %s'%xml_path)
if not self.fake_mode:
self.model.saveModelAsXMLFile(xml_path)
self.log('... done')
self.log('\n')
# model_<modelName>.py
if not self.fake_mode:
xml=open(xml_path)
modelStr=xml.read()
xml.close()
else:
modelStr='fake'
self.createFileFromTemplate(model.model(),
py_path,
namespace={'model_str':modelStr},
overwrite=1)
del modelStr
#-----------------------------------------------------------------
def create_pymodel_file(self, pymodel_path_ori, pymodel_path):
"Creates or overwrite the pymodel"
if os.path.exists(pymodel_path):
self.log('Overwriting %s'%pymodel_path)
else:
self.log('Generating %s'%pymodel_path)
if not self.fake_mode:
import shutil
shutil.copy(pymodel_path_ori, pymodel_path)
self.log('... done')
self.log('\n')
#-----------------------------------------------------------------
# Build as many directories with __init__.py as needed
self.build_package()
# empty files
#self.createEmptyFile("TODO.txt",overwrite=0)
#self.createEmptyFile("README.txt",overwrite=0)
#self.createEmptyFile("VERSION.txt",overwrite=0)
#self.createEmptyFile("INSTALL.txt",overwrite=0)
#self.createEmptyFile("DEPENDENCIES.txt",overwrite=0)
# package's __init__.py
self.createFileFromTemplate(init.init(),"__init__.py",overwrite=0)
# setup.py
self.createFileFromTemplate(setup_tmpl.setup_tmpl(),"setup.py",overwrite=0)
# COMPACT generation scheme
if self.generation_scheme=='compact':
if not self.pymodel_path:
# Create model files (.xml/.py)
xmlp=self.fullPathForGeneratedFile('model_'+self.model.name()+'.xml')
pyp="model_%s.py"%self.model.name()
create_xmlmodel_files(self, xmlp, pyp)
else:
basePath=self.fullPathForGeneratedFile(self.base_dir)
create_pymodel_file(self,
self.pymodel_path,
os.path.join(basePath,
"pymodel_%s.py"%self.model.name()))
# modules
for self._entities in self.entitiesSet():
module_name = self._entities[0].moduleName()
self.createFileFromTemplate(module_compact.module_compact(),
"%s.py" % module_name,
overwrite=0)
# BASE generation scheme
elif self.generation_scheme=='base':
# create 'Base' and model files (.xml/.py)
basePath=self.fullPathForGeneratedFile(self.base_dir)
self.log("Creating directory %s"%basePath)
if not self.fake_mode:
try:
os.mkdir(basePath)
except: self.log('no')
else: self.log('ok')
self.log('\n')
self.createFileFromTemplate(init_base.init_base(),
os.path.join(self.base_dir,"__init__.py"),
overwrite=1)
if not self.pymodel_path:
create_xmlmodel_files(self,
os.path.join(basePath,
'model_'+self.model.name()+'.xml'),
os.path.join(self.base_dir,
"model_%s.py"%self.model.name()))
else:
create_pymodel_file(self,
self.pymodel_path,
os.path.join(basePath,
"pymodel_%s.py"%self.model.name()))
# modules
for self._entities in self.entitiesSet():
module_name = self._entities[0].moduleName()
self.createFileFromTemplate(module_base.module_base(),
"%s.py" % module_name,
overwrite=0)
self.createFileFromTemplate(base_module.base_module(),
os.path.join(self.base_dir,
"%s.py" % module_name),
overwrite=1)
|