Commit acc342ab authored by Nicolas Dumazet's avatar Nicolas Dumazet

move ghostify/unghostify-cation and ghost creation to portal type metaclass

Makes the code more efficient, and much more readable.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@39754 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 77c8b62d
......@@ -7,12 +7,50 @@ from ExtensionClass import ExtensionClass, pmc_init_of
from ZODB.broken import Broken, PersistentBroken
from zLOG import LOG, ERROR, BLATHER
from portal_type_class import generatePortalTypeClass
# PersistentBroken can't be reused directly
# because its « layout differs from 'GhostPortalType' »
ERP5BaseBroken = type('ERP5BaseBroken', (Broken, ERP5Base), dict(x
for x in PersistentBroken.__dict__.iteritems()
if x[0] not in ('__dict__', '__module__', '__weakref__')))
class GhostPortalType(ERP5Base): #SimpleItem
"""
Ghost state for a portal type that is not loaded.
One instance of this class exists per portal type class on the system.
When an object of this portal type is loaded (a new object is created,
or an attribute of an existing object is accessed) this class will
change the bases of the portal type class so that it points to the
correct Document+Mixin+interfaces+AccessorHolder classes.
"""
def __init__(self, *args, **kw):
self.__class__.loadClass()
getattr(self, '__init__')(*args, **kw)
def __getattribute__(self, attr):
"""
This is only called once to load the class.
Because __bases__ is changed, the behavior of this object
will change after the first call.
"""
# Class must be loaded if '__of__' is requested because otherwise,
# next call to __getattribute__ would lose any acquisition wrapper.
if attr in ('__class__',
'__getnewargs__',
'__getstate__',
'__dict__',
'__module__',
'__name__',
'__repr__',
'__str__') or attr[:3] in ('_p_', '_v_'):
return super(GhostPortalType, self).__getattribute__(attr)
#LOG("ERP5Type.Dynamic", BLATHER,
# "loading attribute %s.%s..." % (name, attr))
self.__class__.loadClass()
return getattr(self, attr)
class PortalTypeMetaClass(ExtensionClass):
"""
Meta class that will be used by portal type classes
......@@ -54,71 +92,44 @@ class PortalTypeMetaClass(ExtensionClass):
pmc_init_of(subcls)
InitializeClass(subcls)
def generateLazyPortalTypeClass(portal_type_name,
portal_type_class_loader):
def load(self, attr):
# self might be a subclass of a portal type class
# we need to find the right parent class to change
for klass in self.__class__.__mro__:
# XXX hardcoded, this doesnt look too good
if klass.__module__ == "erp5.portal_type":
break
else:
raise AttributeError("Could not find a portal type class in class hierarchy")
portal_type = klass.__name__
try:
baseclasses, attributes = portal_type_class_loader(portal_type)
except AttributeError:
LOG("ERP5Type.Dynamic", ERROR,
"Could not access Portal Type Object for type %r"
% portal_type, error=sys.exc_info())
baseclasses = (ERP5BaseBroken, )
attributes = {}
# save the old bases to be able to restore a ghost state later
klass.__ghostbase__ = klass.__bases__
klass.__bases__ = baseclasses
for key, value in attributes.iteritems():
setattr(klass, key, value)
klass.resetAcquisitionAndSecurity()
return getattr(self, attr)
class GhostPortalType(ERP5Base): #SimpleItem
"""
Ghost state for a portal type that is not loaded.
One instance of this class exists per portal type class on the system.
When an object of this portal type is loaded (a new object is created,
or an attribute of an existing object is accessed) this class will
change the bases of the portal type class so that it points to the
correct Document+Mixin+interfaces+AccessorHolder classes.
"""
def __init__(self, *args, **kw):
load(self, '__init__')(*args, **kw)
def __getattribute__(self, attr):
"""
This is only called once to load the class.
Because __bases__ is changed, the behavior of this object
will change after the first call.
"""
# Class must be loaded if '__of__' is requested because otherwise,
# next call to __getattribute__ would lose any acquisition wrapper.
if attr in ('__class__',
'__getnewargs__',
'__getstate__',
'__dict__',
'__module__',
'__name__',
'__repr__',
'__str__') or attr[:3] in ('_p_', '_v_'):
return super(GhostPortalType, self).__getattribute__(attr)
#LOG("ERP5Type.Dynamic", BLATHER,
# "loading attribute %s.%s..." % (name, attr))
return load(self, attr)
return PortalTypeMetaClass(portal_type_name, (GhostPortalType,), dict())
def restoreGhostState(cls):
ghostbase = getattr(cls, '__ghostbase__', None)
if ghostbase is not None:
for attr in cls.__dict__.keys():
if attr not in ('__module__', '__doc__',):
delattr(cls, attr)
cls.__bases__ = ghostbase
cls.resetAcquisitionAndSecurity()
def loadClass(cls):
# cls might be a subclass of a portal type class
# we need to find the right class to change
for klass in cls.__mro__:
# XXX hardcoded, this doesnt look too good
if klass.__module__ == "erp5.portal_type":
break
else:
raise AttributeError("Could not find a portal type class in"
" class hierarchy")
portal_type = klass.__name__
try:
baseclasses, attributes = generatePortalTypeClass(portal_type)
except AttributeError:
LOG("ERP5Type.Dynamic", ERROR,
"Could not access Portal Type Object for type %r"
% portal_type, error=sys.exc_info())
baseclasses = (ERP5BaseBroken, )
attributes = {}
# save the old bases to be able to restore a ghost state later
klass.__ghostbase__ = klass.__bases__
klass.__bases__ = baseclasses
for key, value in attributes.iteritems():
setattr(klass, key, value)
klass.resetAcquisitionAndSecurity()
def generateLazyPortalTypeClass(portal_type_name):
return PortalTypeMetaClass(portal_type_name, (GhostPortalType,), {})
......@@ -33,7 +33,6 @@ import inspect
from types import ModuleType
from dynamic_module import registerDynamicModule
from lazy_class import generateLazyPortalTypeClass
from Products.ERP5Type.Base import _aq_reset
from Products.ERP5Type.Globals import InitializeClass
......@@ -182,6 +181,7 @@ def generatePortalTypeClass(portal_type_name):
return tuple(baseclasses), dict(portal_type=portal_type_name)
from lazy_class import generateLazyPortalTypeClass
def initializeDynamicModules():
"""
Create erp5 module and its submodules
......@@ -201,13 +201,6 @@ def initializeDynamicModules():
XXX: there should be only one accessor_holder once the code is
stable and all the Property Sheets have been migrated
"""
def loadPortalTypeClass(portal_type_name):
"""
Returns a lazily-loaded "portal-type as a class"
"""
return generateLazyPortalTypeClass(portal_type_name,
generatePortalTypeClass)
erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5
erp5.document = ModuleType("erp5.document")
......@@ -219,7 +212,7 @@ def initializeDynamicModules():
sys.modules["erp5.filesystem_accessor_holder"] = erp5.filesystem_accessor_holder
portal_type_container = registerDynamicModule('erp5.portal_type',
loadPortalTypeClass)
generateLazyPortalTypeClass)
erp5.portal_type = portal_type_container
......@@ -302,13 +295,7 @@ def synchronizeDynamicModules(context, force=False):
for class_name, klass in inspect.getmembers(erp5.portal_type,
inspect.isclass):
ghostbase = getattr(klass, '__ghostbase__', None)
if ghostbase is not None:
for attr in klass.__dict__.keys():
if attr != '__module__':
delattr(klass, attr)
klass.__bases__ = ghostbase
klass.resetAcquisitionAndSecurity()
klass.restoreGhostState()
# Clear accessor holders of ZODB Property Sheets
_clearAccessorHolderModule(erp5.zodb_accessor_holder)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment