From acc342ab298d2e9b7fbb2829ff60ca82a9aca011 Mon Sep 17 00:00:00 2001 From: Nicolas Dumazet <nicolas.dumazet@nexedi.com> Date: Tue, 2 Nov 2010 07:08:58 +0000 Subject: [PATCH] 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 --- product/ERP5Type/dynamic/lazy_class.py | 147 ++++++++++-------- product/ERP5Type/dynamic/portal_type_class.py | 19 +-- 2 files changed, 82 insertions(+), 84 deletions(-) diff --git a/product/ERP5Type/dynamic/lazy_class.py b/product/ERP5Type/dynamic/lazy_class.py index 331634a075..e1a2094add 100644 --- a/product/ERP5Type/dynamic/lazy_class.py +++ b/product/ERP5Type/dynamic/lazy_class.py @@ -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,), {}) diff --git a/product/ERP5Type/dynamic/portal_type_class.py b/product/ERP5Type/dynamic/portal_type_class.py index e9eb3ee78d..60c08c8e01 100644 --- a/product/ERP5Type/dynamic/portal_type_class.py +++ b/product/ERP5Type/dynamic/portal_type_class.py @@ -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) -- 2.30.9