#! /usr/bin/env python
# -*- 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.
#-----------------------------------------------------------------------------
"""
Documentation not written yet --see doc/README.dynamic.txt for details.
"""
import code, new, sys
from Modeling.CustomObject import CustomObject
from Modeling.ModelSet import defaultModelSet
from Modeling.utils import capitalizeFirstLetter
def function_for_code(c, func_name):
c+='\nfunc='+func_name+'\n'
exec(c)
return func
def instance_method(aClass, c, func_name):
func=function_for_code(c, func_name)
meth=new.instancemethod(func, None, aClass)
return meth
def init_code(entity, init=None):
"""
init: the original __init__, if available
"""
c="def __init__(self, **kw):\n"
for p in entity.classProperties():
c+=" self._"+p.name()+"="
if hasattr(p,'isToMany'): # rel
if p.isToMany():
c+="()"
else:
c+="None"
else:
c+=repr(p.defaultValue())
c+="\n"
c+=""" for k,v in kw.items():
self.takeValueForKey(v, k)
"""
if init:
c+=" apply(self.%s, (), kw)"%init
return c
def add_init(aClass, entity):
"Adds to the class the method __init__() suitable for entity"
c=init_code(entity)
m=instance_method(aClass, c, "__init__")
setattr(aClass,'__init__',m)
def define_entityName(aClass, entity):
c="""def entityName(self):
"Used by the framework to link this object to its entity"
return "%s" # do not change
"""%entity.name()
return c
def add_entityName(aClass, entity):
"Adds to the new class the method entityName() suitable for entity"
c=define_entityName(aClass, entity)
m=instance_method(aClass, c, "entityName")
setattr(aClass,'entityName',m)
def add_getters(aClass, entity):
"Adds getters to aClass, one for each entity.classProperties()"
for p in entity.classProperties():
add_getter(aClass, p)
def add_setters(aClass, entity):
"Adds setters to aClass, one for each entity.classProperties()"
for p in entity.classProperties():
add_setter(aClass, p)
def getter_code(prop):
func_name="get"+capitalizeFirstLetter(prop.name())
c='def %s(self):\n self.willRead()\n'%func_name
c+=" return self._"+prop.name()
return func_name, c
def add_getter(aClass, prop):
"Builds and adds to aClass the getter for property prop"
func_name, c=getter_code(prop)
m=instance_method(aClass, c, func_name)
setattr(aClass, m.__name__, m)
def setters_code(prop):
"returns: ( (name, code_string), ... )"
names_n_funcs=[]
part_func_name=capitalizeFirstLetter(prop.name())
if hasattr(prop,'isToMany') and prop.isToMany():
func_name="addTo"+part_func_name
c='''def %(func_name)s(self, obj):
if obj not in self._%(name)s:
self.willChange()
_list=list(self._%(name)s)
_list.append(obj)
self._%(name)s=tuple(_list)
'''%{'func_name': func_name, 'name': prop.name()}
names_n_funcs.append( (func_name, c) )
func_name="removeFrom"+part_func_name
c='''def %(func_name)s(self, obj):
self.willChange()
_list=list(self._%(name)s)
_list.remove(obj)
self._%(name)s=tuple(_list)
'''%{'func_name': func_name, 'name': prop.name()}
names_n_funcs.append( (func_name, c) )
func_name="set"+part_func_name
c='def %s(self, obj):\n self.willChange()\n'%func_name
c+=" self._"+prop.name()+"=obj"
names_n_funcs.append( (func_name, c) )
return names_n_funcs
def add_setter(aClass, prop):
"Builds and adds to aClass the setter for property prop"
names_n_funcs=setters_code(prop)
for func_name, c in names_n_funcs:
m=instance_method(aClass, c, func_name)
setattr(aClass, m.__name__, m)
def add_properties(aClass, entity):
for p in entity.classProperties():
#print 'defining prop: ', p.name()
part_func_name=capitalizeFirstLetter(p.name())
prop=property(getattr(aClass, 'get'+part_func_name),
getattr(aClass, 'set'+part_func_name), None)
setattr(aClass, p.name(), prop)
def build(model, define_properties=0):
"""
Takes a model and builds the corresponding package and modules
:Parameters:
- `model`: either a `Modeling.Model.Model` or a
`Modeling.PyModel.Model`. Please note that if it is a pymodel, its
method build() is called
- `define_properties`:
"""
from Modeling import PyModel
if isinstance(model, PyModel.Model):
if not model.is_built:
model.build()
model=model.component
module_name=model.packageName()
classes={}
modules={}
for e in model.entities():
c=new.classobj(e.name(), (CustomObject,), {})
m=new.module(e.name())
setattr(m, e.name(),c)
classes[e.name()]=c
modules[e.name()]=m
m.__name__= module_name+'.'+e.name()
add_init(c, e)
add_entityName(c, e)
add_getters(c, e)
add_setters(c, e)
if define_properties:
add_properties(c, e)
c.__module__=module_name+'.'+e.name()
p=new.module(module_name)
#m.Book=classes['Book']
import sys
for name,m in modules.items():
setattr(p,name,m)
sys.modules[module_name]=p
# the following is needed if we want to "from AB.Book import Book"
for name,m in modules.items():
sys.modules[module_name+'.'+name]=m
##
## Examples of use follow
##
if __name__ == "__main__":
from Modeling import ModelSet,Model
model_name="AuthorBooks"
if ModelSet.defaultModelSet().modelNamed(model_name) is None:
import os
model=Model.searchModel(model_name, 'xmlmodels', verbose=1)
if not model:
raise RuntimeError, "Couldn't load model '%s'"%model_name
else:
ModelSet.defaultModelSet().addModel(model)
build(model)
from Modeling.EditingContext import EditingContext
ec=EditingContext()
print [b.getTitle() for b in ec.fetch('Book')]
from AuthorBooks import Book
from AuthorBooks.Book import Book
print Book
b=Book(title='mon titre')
print repr(b.getTitle())
sys.exit(0)
########################################################################
# metaclass
#
def info(classdict, msg):
if classdict.get('mdl_verbose_metaclass', None):
sys.stderr.write("[CustomObjectMeta] "+msg+'\n')
def checkEntityName(classname, bases, classdict):
entityName=classdict.get('entityName', None)
if entityName is None:
info(classdict, 'entityName is not present: searching base classes')
for b in bases:
if b==CustomObject:
continue;
entityName=getattr(b, 'entityName',None)
if entityName:
info(classdict, 'Found in %s'%b.__name__)
if entityName is None:
info(classdict, 'entityName is not present: using default: %s'%classname)
entityName=classname
if not callable(entityName):
classdict['entityName']=lambda self, n=entityName: n
else:
entityName=entityName.im_func(None)
return entityName
def addCustomObject(bases):
from Modeling.CustomObject import CustomObject
if CustomObject not in bases:
bases=list(bases)
bases.append(CustomObject)
bases=tuple(bases)
return bases
def check_oldinit(classname, oldinit, entity):
"Check that the already defined __init__ can be called without any arguments"
import inspect
args, varargs, varkw, defaults=inspect.getargspec(oldinit)
if len(args)-len(defaults)>1:
raise RuntimeError,'%s.__init__() cannot be called without arguments'%classname
# to be continued: on veut pouvoir appeler init() avec les bons arguments,
# tout en rcuprant ceux qui peuvent nous servir (les attributs)
# e.g. init(self, attr1='t') ou init(self, attr1='x', non_attr='xx') etc.
def define_init(entity, classname, classdict):
oldinit=classdict.get('__init__', None)
if oldinit:
classdict['__original_init__']=oldinit
check_oldinit(classname, oldinit, entity)
c=init_code(entity, oldinit)
init=function_for_code(c, '__init__')
classdict['__init__']=init
def define_getter(prop, classdict):
func_name, c=getter_code(prop)
info(classdict, "adding getter: %s"%func_name)
getter=function_for_code(c, func_name)
classdict[func_name]=getter
def define_getters(entity, classname, classdict):
info(classdict, "setting getters")
for p in entity.classProperties():
define_getter(p, classdict)
def define_setter(prop, classdict):
names_and_funcs=setters_code(prop)
for func_name, c in names_and_funcs:
info(classdict, "adding setter: %s"%func_name)
setter=function_for_code(c, func_name)
classdict[func_name]=setter
def define_setters(entity, classname, classdict):
info(classdict, "setting setters")
for p in entity.classProperties():
define_setter(p, classdict)
def define_properties(entity, classname, classdict):
#info(classdict, "defining properties for class %s"%classname)
for p in entity.classProperties():
info(classdict, "adding properties '%s' for class %s"%(p.name(),classname))
part_func_name=capitalizeFirstLetter(p.name())
prop=property(classdict['get'+part_func_name],
classdict['set'+part_func_name], None)
classdict[p.name()]=prop
try:
class A(type): pass
metaclass_available=1
del A
except TypeError:
metaclass_available=0
class EntityNotFound(RuntimeError): pass
if metaclass_available:
class CustomObjectMeta(type):
def __new__(meta,classname,bases,classdict):
info(classdict, 'meta: %s'%meta)
info(classdict, 'classname: '+classname)
info(classdict, 'bases: %s'%(bases,))
info(classdict, 'classdict: %s'%classdict)
entityName=checkEntityName(classname, bases, classdict)
bases=addCustomObject(bases)
from Modeling.ModelSet import defaultModelSet
entity=defaultModelSet().entityNamed(entityName)
if entity is None:
raise EntityNotFound, 'Unable to initialize class %s: entity %s not found'%(classname, entityName)
else:
define_init(entity, classname, classdict)
define_getters(entity, classname, classdict)
define_setters(entity, classname, classdict)
if classdict.get('mdl_define_properties', None):
define_properties(entity, classname, classdict)
return super(CustomObjectMeta,meta).__new__(meta,classname,
bases,classdict)
def build_with_metaclass(model, define_properties=0, verbose=0):
module_name=model.packageName()
classes={}
modules={}
for e in model.entities():
c=CustomObjectMeta(e.className(), (),
{'mdl_verbose_metaclass': verbose,
'mdl_define_properties': define_properties})
m=new.module(e.name())
setattr(m, e.name(),c)
classes[e.name()]=c
modules[e.name()]=m
m.__name__=model.packageName()+'.'+e.name() # pas requis? mais conforme
p=new.module(module_name)
#m.Book=classes['Book']
import sys
for name,m in modules.items():
setattr(p,name,m)
sys.modules[module_name]=p
# the following is needed if we want to "from AuthorBooks.Book import Book"
for name,m in modules.items():
sys.modules[module_name+'.'+name]=m
else:
def build_with_metaclass(model, define_properties=0, verbose=0):
raise NotImplementedError, "metaclass are not available"
##
## Two examples of use follow
##
if __name__ == "__main__2":
from Modeling import ModelSet,Model
model_name="AuthorBooks"
if ModelSet.defaultModelSet().modelNamed(model_name) is None:
import os
mydir = '.'
model=Model.searchModel(model_name, mydir, verbose=1)
if not model:
raise RuntimeError, "Couldn't load model '%s'"%model_name
else:
ModelSet.defaultModelSet().addModel(model)
class Book:
__metaclass__=CustomObjectMeta
mdl_verbose_metaclass=1
mdl_define_properties=1
entityName='Book'
#def entityName(self):
# return 'Book'
#def __init__(self):
# print 'PPP'
def willRead(self):
print '[willRead]',
def willChange(self):
print '[willChange]',
def _getTitle(self):
print '[_getTitle()]',
return self._title
def _setTitle(self, title):
print '[_setTitle()]',
self._title=title
#__metaclass__=CustomObjectMeta
#c=type('Writer', (), {'__metaclass__': CustomObjectMeta,
# 'mdl_verbose_metaclass': 1,
# 'mdl_define_properties': 1})
Writer=CustomObjectMeta('Writer', (), {'mdl_verbose_metaclass': 1,
'mdl_define_properties': 0})
#Book=CustomObjectMeta('Book', (), {'mdl_verbose_metaclass': 1})
print Writer, Writer.__bases__
from Modeling.EditingContext import EditingContext
ec=EditingContext()
print Book.__bases__
b=Book(title='glop')
ec.insert(b)
print b.entityName()
print b._title
b.setTitle('title test')
print b.getTitle()
print ec.allInsertedObjects(), b
print 'trying properties'
print b.title
print '## KVC'
print 'storedValue ',b.storedValueForKey('title')
print 'takestored ',b.takeStoredValueForKey('takeStoredValueForKey','title')
print 'value ',b.valueForKey('title')
print 'takeValue ',b.takeValueForKey('takeStoredValueForKey','title')
print 'title: ', b.title
sys.exit(0)
if __name__ == "__main__":
from Modeling import ModelSet,Model
model_name="AuthorBooks"
if ModelSet.defaultModelSet().modelNamed(model_name) is None:
import os
mydir = '.'
model=Model.searchModel(model_name, mydir, verbose=1)
if not model:
raise RuntimeError, "Couldn't load model '%s'"%model_name
else:
ModelSet.defaultModelSet().addModel(model)
build_with_metaclass(model, define_properties=0)
from Modeling.EditingContext import EditingContext
ec=EditingContext()
print [b.getTitle() for b in ec.fetch('Book')]
from AuthorBooks import Book
from AuthorBooks.Book import Book
print Book
b=Book(title='mon titre')
print repr(b.getTitle()), b.title
sys.exit(0)
|