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 ...@@ -7,12 +7,50 @@ from ExtensionClass import ExtensionClass, pmc_init_of
from ZODB.broken import Broken, PersistentBroken from ZODB.broken import Broken, PersistentBroken
from zLOG import LOG, ERROR, BLATHER from zLOG import LOG, ERROR, BLATHER
from portal_type_class import generatePortalTypeClass
# PersistentBroken can't be reused directly # PersistentBroken can't be reused directly
# because its « layout differs from 'GhostPortalType' » # because its « layout differs from 'GhostPortalType' »
ERP5BaseBroken = type('ERP5BaseBroken', (Broken, ERP5Base), dict(x ERP5BaseBroken = type('ERP5BaseBroken', (Broken, ERP5Base), dict(x
for x in PersistentBroken.__dict__.iteritems() for x in PersistentBroken.__dict__.iteritems()
if x[0] not in ('__dict__', '__module__', '__weakref__'))) 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): class PortalTypeMetaClass(ExtensionClass):
""" """
Meta class that will be used by portal type classes Meta class that will be used by portal type classes
...@@ -54,71 +92,44 @@ class PortalTypeMetaClass(ExtensionClass): ...@@ -54,71 +92,44 @@ class PortalTypeMetaClass(ExtensionClass):
pmc_init_of(subcls) pmc_init_of(subcls)
InitializeClass(subcls) InitializeClass(subcls)
def generateLazyPortalTypeClass(portal_type_name, def restoreGhostState(cls):
portal_type_class_loader): ghostbase = getattr(cls, '__ghostbase__', None)
def load(self, attr): if ghostbase is not None:
# self might be a subclass of a portal type class for attr in cls.__dict__.keys():
# we need to find the right parent class to change if attr not in ('__module__', '__doc__',):
for klass in self.__class__.__mro__: delattr(cls, attr)
# XXX hardcoded, this doesnt look too good cls.__bases__ = ghostbase
if klass.__module__ == "erp5.portal_type": cls.resetAcquisitionAndSecurity()
break
else: def loadClass(cls):
raise AttributeError("Could not find a portal type class in class hierarchy") # cls might be a subclass of a portal type class
# we need to find the right class to change
portal_type = klass.__name__ for klass in cls.__mro__:
try: # XXX hardcoded, this doesnt look too good
baseclasses, attributes = portal_type_class_loader(portal_type) if klass.__module__ == "erp5.portal_type":
except AttributeError: break
LOG("ERP5Type.Dynamic", ERROR, else:
"Could not access Portal Type Object for type %r" raise AttributeError("Could not find a portal type class in"
% portal_type, error=sys.exc_info()) " class hierarchy")
baseclasses = (ERP5BaseBroken, )
attributes = {} portal_type = klass.__name__
try:
# save the old bases to be able to restore a ghost state later baseclasses, attributes = generatePortalTypeClass(portal_type)
klass.__ghostbase__ = klass.__bases__ except AttributeError:
klass.__bases__ = baseclasses LOG("ERP5Type.Dynamic", ERROR,
"Could not access Portal Type Object for type %r"
for key, value in attributes.iteritems(): % portal_type, error=sys.exc_info())
setattr(klass, key, value) baseclasses = (ERP5BaseBroken, )
attributes = {}
klass.resetAcquisitionAndSecurity()
# save the old bases to be able to restore a ghost state later
return getattr(self, attr) klass.__ghostbase__ = klass.__bases__
klass.__bases__ = baseclasses
class GhostPortalType(ERP5Base): #SimpleItem
""" for key, value in attributes.iteritems():
Ghost state for a portal type that is not loaded. setattr(klass, key, value)
One instance of this class exists per portal type class on the system. klass.resetAcquisitionAndSecurity()
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 def generateLazyPortalTypeClass(portal_type_name):
change the bases of the portal type class so that it points to the return PortalTypeMetaClass(portal_type_name, (GhostPortalType,), {})
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())
...@@ -33,7 +33,6 @@ import inspect ...@@ -33,7 +33,6 @@ import inspect
from types import ModuleType from types import ModuleType
from dynamic_module import registerDynamicModule from dynamic_module import registerDynamicModule
from lazy_class import generateLazyPortalTypeClass
from Products.ERP5Type.Base import _aq_reset from Products.ERP5Type.Base import _aq_reset
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
...@@ -182,6 +181,7 @@ def generatePortalTypeClass(portal_type_name): ...@@ -182,6 +181,7 @@ def generatePortalTypeClass(portal_type_name):
return tuple(baseclasses), dict(portal_type=portal_type_name) return tuple(baseclasses), dict(portal_type=portal_type_name)
from lazy_class import generateLazyPortalTypeClass
def initializeDynamicModules(): def initializeDynamicModules():
""" """
Create erp5 module and its submodules Create erp5 module and its submodules
...@@ -201,13 +201,6 @@ def initializeDynamicModules(): ...@@ -201,13 +201,6 @@ def initializeDynamicModules():
XXX: there should be only one accessor_holder once the code is XXX: there should be only one accessor_holder once the code is
stable and all the Property Sheets have been migrated 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") erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5 sys.modules["erp5"] = erp5
erp5.document = ModuleType("erp5.document") erp5.document = ModuleType("erp5.document")
...@@ -219,7 +212,7 @@ def initializeDynamicModules(): ...@@ -219,7 +212,7 @@ def initializeDynamicModules():
sys.modules["erp5.filesystem_accessor_holder"] = erp5.filesystem_accessor_holder sys.modules["erp5.filesystem_accessor_holder"] = erp5.filesystem_accessor_holder
portal_type_container = registerDynamicModule('erp5.portal_type', portal_type_container = registerDynamicModule('erp5.portal_type',
loadPortalTypeClass) generateLazyPortalTypeClass)
erp5.portal_type = portal_type_container erp5.portal_type = portal_type_container
...@@ -302,13 +295,7 @@ def synchronizeDynamicModules(context, force=False): ...@@ -302,13 +295,7 @@ def synchronizeDynamicModules(context, force=False):
for class_name, klass in inspect.getmembers(erp5.portal_type, for class_name, klass in inspect.getmembers(erp5.portal_type,
inspect.isclass): inspect.isclass):
ghostbase = getattr(klass, '__ghostbase__', None) klass.restoreGhostState()
if ghostbase is not None:
for attr in klass.__dict__.keys():
if attr != '__module__':
delattr(klass, attr)
klass.__bases__ = ghostbase
klass.resetAcquisitionAndSecurity()
# Clear accessor holders of ZODB Property Sheets # Clear accessor holders of ZODB Property Sheets
_clearAccessorHolderModule(erp5.zodb_accessor_holder) _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