[Erp5-report] r43892 arnaud.fontaine - in /erp5/trunk/products/ERP5Type: ./ Core/ dynamic/ ...
nobody at svn.erp5.org
nobody at svn.erp5.org
Wed Mar 2 13:19:02 CET 2011
Author: arnaud.fontaine
Date: Wed Mar 2 13:19:02 2011
New Revision: 43892
URL: http://svn.erp5.org?rev=43892&view=rev
Log:
Accessors generation is now performed in StandardProperty, AcquiredProperty,
CategoryProperty and DynamicCategoryProperty rather than setDefaultProperties
from Utils.
erp5.accessor_holder has also been split up into two additional modules, namely
erp5.accessor_holder.property_sheet, containing accessor holders for ZODB
Property Sheets, and erp5.accessor_holder.portal_type, containing accessor
holders specific to the Portal Types (only being used by PreferenceTool and egov
for now). erp5.accessor_holder only contains accessor holders common to both
Portal Types and Property Sheets (such as BaseAccessorHolder).
This commit also enables code committed in r43886.
Modified:
erp5/trunk/products/ERP5Type/Base.py
erp5/trunk/products/ERP5Type/Core/PropertySheet.py
erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py
erp5/trunk/products/ERP5Type/dynamic/lazy_class.py
erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py
erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py
Modified: erp5/trunk/products/ERP5Type/Base.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Base.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Base.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Base.py [utf8] Wed Mar 2 13:19:02 2011
@@ -325,92 +325,21 @@ class PropertyHolder(object):
return [x for x in self.__dict__.items() if x[0] not in
PropertyHolder.RESERVED_PROPERTY_SET]
- # Accessor generation
- def createAccessor(self, id):
- """
- Invokes appropriate factory and create an accessor
- """
- fake_accessor = getattr(self, id)
- ptype = getattr(self, 'portal_type', None)
- if ptype is None:
- ptype = self._portal_type
- if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
- # Case 1 : a workflow method only
- accessor = Base._doNothing
- else:
- # Case 2 : a workflow method over an accessor
- (accessor_class, accessor_args, key) = fake_accessor
- accessor = accessor_class(id, key, *accessor_args)
- for wf_id, tr_id, once in self.workflow_method_registry.get(id, ()):
- if not isinstance(accessor, WorkflowMethod):
- accessor = WorkflowMethod(accessor)
- if once:
- accessor.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
- else:
- accessor.registerTransitionAlways(ptype, wf_id, tr_id)
- else:
- if once:
- accessor.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
- else:
- accessor.registerTransitionAlways(ptype, wf_id, tr_id)
- setattr(self, id, accessor)
- return accessor
-
- def registerAccessor(self, id, key, accessor_class, accessor_args):
- """
- Saves in a dictionary all parameters required to create an accessor
- The goal here is to minimize memory occupation. We have found the following:
-
- - the size of a tuple with simple types and the size
- of None are the same (a pointer)
-
- - the size of a pointer to a class is the same as the
- size of None
-
- - the python caching system for tuples is efficient for tuples
- which contain simple types (string, int, etc.) but innefficient
- for tuples which contain a pointer
-
- - as a result, it is better to create separate dicts if
- values contain pointers and single dict if value is
- a tuple of simple types
-
- Parameters:
-
- id -- The id the accessor (ex. getFirstName)
-
- key -- The id of the property (ex. first_name) or the id of the
- method for Alias accessors
- """
- #LOG('registerAccessor', 0, "%s %s %s" % (id , self._portal_type, accessor_args))
- # First we try to compress the information required
- # to build a new accessor in such way that
- # if the same information is provided twice, we
- # shall keep it once only
- new_accessor_args = []
- for arg in accessor_args:
- if type(arg) is types.ListType:
- new_accessor_args.append(tuple(arg))
- else:
- new_accessor_args.append(arg)
- accessor_args = tuple(new_accessor_args)
- original_registration_tuple = (accessor_class, accessor_args, key)
- registration_tuple = method_registration_cache.get(original_registration_tuple)
- if registration_tuple is None:
- registration_tuple = original_registration_tuple
- method_registration_cache[registration_tuple] = registration_tuple
- # Use the cached tuple (same value, different pointer)
- setattr(self, id, registration_tuple)
-
def registerWorkflowMethod(self, id, wf_id, tr_id, once_per_transaction=0):
- #LOG('registerWorkflowMethod', 0, "%s %s %s %s %s" % (self._portal_type, id, wf_id, tr_id, once_per_transaction))
- signature = (wf_id, tr_id, once_per_transaction)
- signature_list = self.workflow_method_registry.get(id, ())
- if signature not in signature_list:
- self.workflow_method_registry[id] = signature_list + (signature,)
- if getattr(self, id, None) is None:
- setattr(self, id, PropertyHolder.WORKFLOW_METHOD_MARKER)
- self.createAccessor(id)
+ portal_type = self.portal_type
+
+ workflow_method = getattr(self, id, None)
+ if workflow_method is None:
+ workflow_method = WorkflowMethod(Base._doNothing)
+ setattr(self, id, workflow_method)
+ if once_per_transaction:
+ workflow_method.registerTransitionOncePerTransaction(portal_type,
+ wf_id,
+ tr_id)
+ else:
+ workflow_method.registerTransitionAlways(portal_type,
+ wf_id,
+ tr_id)
def declareProtected(self, permission, accessor_name):
"""
@@ -520,23 +449,6 @@ def getClassPropertyList(klass):
if p not in ps_list])
return ps_list
-def initializeClassDynamicProperties(self, klass):
- if klass not in Base.aq_method_generated:
- # Recurse to superclasses
- for super_klass in klass.__bases__:
- if getattr(super_klass, 'isRADContent', 0):
- initializeClassDynamicProperties(self, super_klass)
- # Initialize default properties
- from Utils import setDefaultClassProperties
- if not getattr(klass, 'isPortalContent', None):
- if getattr(klass, 'isRADContent', 0):
- setDefaultClassProperties(klass)
- # Mark as generated
- Base.aq_method_generated.add(klass)
-
-def initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal):
- raise ValueError("No reason to go through this no more with portal type classes")
-
def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
"""We should now make sure workflow methods are defined
and also make sure simulation state is defined."""
@@ -567,10 +479,8 @@ def initializePortalTypeDynamicWorkflowM
if not hasattr(ptype_klass, method_id):
method = getter(method_id, wf_id)
# Attach to portal_type
- setattr(ptype_klass, method_id, method)
- ptype_klass.security.declareProtected(
- Permissions.AccessContentsInformation,
- method_id )
+ ptype_klass.registerAccessor(method,
+ Permissions.AccessContentsInformation)
storage = dc_workflow_dict
transitions = wf.transitions
@@ -806,12 +716,6 @@ class Base( CopyContainer,
self._setDescription(value)
self.reindexObject()
- security.declareProtected( Permissions.AccessContentsInformation, 'test_dyn' )
- def test_dyn(self):
- """
- """
- initializeClassDynamicProperties(self, self.__class__)
-
security.declarePublic('provides')
def provides(cls, interface_name):
"""
@@ -848,18 +752,6 @@ class Base( CopyContainer,
pformat(rev1.__dict__),
pformat(rev2.__dict__)))
- def initializePortalTypeDynamicProperties(self):
- """
- Test purpose
- """
- ptype = self.portal_type
- klass = self.__class__
- aq_key = self._aq_key()
- initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, \
- self.getPortalObject())
- from Products.ERP5Form.PreferenceTool import createPreferenceToolAccessorList
- createPreferenceToolAccessorList(self.getPortalObject())
-
def _aq_dynamic(self, id):
# ahah! disabled, thanks to portal type classes
return None
Modified: erp5/trunk/products/ERP5Type/Core/PropertySheet.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/PropertySheet.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/PropertySheet.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Core/PropertySheet.py [utf8] Wed Mar 2 13:19:02 2011
@@ -48,6 +48,7 @@ class PropertySheet(Folder):
security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation)
+ # TODO: REMOVE
security.declareProtected(Permissions.AccessContentsInformation,
'exportToFilesystemDefinition')
def exportToFilesystemDefinition(self):
@@ -86,22 +87,15 @@ class PropertySheet(Folder):
return (properties, categories, constraints)
security.declarePrivate('createAccessorHolder')
- def createAccessorHolder(self):
+ def createAccessorHolder(self, expression_context, portal):
"""
- Create a new accessor holder from the Property Sheet (the
- accessors are created through a Property Holder)
+ Create a new accessor holder from the Property Sheet
"""
- property_holder = PropertyHolder(self.getId())
+ accessor_holder = AccessorHolderType(self.getId())
- # Prepare the Property Holder
- property_holder._properties, \
- property_holder._categories, \
- property_holder._constraints = self.exportToFilesystemDefinition()
-
- return AccessorHolderType.fromPropertyHolder(
- property_holder,
- self.getPortalObject(),
- 'erp5.accessor_holder')
+ self.applyOnAccessorHolder(accessor_holder, expression_context, portal)
+
+ return accessor_holder
@staticmethod
def _guessFilesystemPropertyPortalType(attribute_dict):
Modified: erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py [utf8] Wed Mar 2 13:19:02 2011
@@ -33,102 +33,74 @@ Accessor Holders, that is, generation of
* Utils, Property Sheet Tool can be probably be cleaned up as well by
moving specialized code here.
"""
-import sys
+from types import ModuleType
from Products.ERP5Type import Permissions
-from Products.ERP5Type.Base import PropertyHolder, Base
-from Products.ERP5Type.Utils import createRelatedAccessors, createExpressionContext
-from Products.ERP5Type.Utils import setDefaultClassProperties, setDefaultProperties
+from Products.ERP5Type.Utils import createExpressionContext
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import UpperCase
from Products.ERP5Type.Accessor import Related, RelatedValue
+from AccessControl import ClassSecurityInfo
-from zLOG import LOG, ERROR, INFO
+from zLOG import LOG, ERROR, INFO, WARNING
class AccessorHolderType(type):
_skip_permission_tuple = (Permissions.AccessContentsInformation,
Permissions.ModifyPortalContent)
- def _next_registerAccessor(cls,
+ def registerAccessor(cls,
accessor,
- permission):
+ permission=None):
accessor_name = accessor.__name__
setattr(cls, accessor_name, accessor)
+ if permission is None:
+ return
# private accessors do not need declarative security
if accessor_name[0] != '_' and \
permission not in AccessorHolderType._skip_permission_tuple:
cls.security.declareProtected(permission, accessor_name)
- @classmethod
- def fromPropertyHolder(meta_type,
- property_holder,
- portal=None,
- accessor_holder_module_name=None,
- initialize=True):
+ def __new__(meta_class, class_name, base_tuple=(object,), attribute_dict={}):
+ # we dont want to add several times to the same list, so make sure
+ # that duplicate attributes just point to the same list object
+ constraint_list = []
+ attribute_dict.update(_categories=[],
+ _constraints=constraint_list,
+ constraints=constraint_list,
+ security=ClassSecurityInfo(),
+ _properties=[])
+
+ return super(AccessorHolderType, meta_class).__new__(meta_class,
+ class_name,
+ base_tuple,
+ attribute_dict)
+
+ def _finalize(cls):
+ cls.security.apply(cls)
+ InitializeClass(cls)
+
+class AccessorHolderModuleType(ModuleType):
+ def registerAccessorHolder(self, accessor_holder):
"""
- Create a new accessor holder class from the given Property Holder
- within the given accessor holder module
+ Add an accessor holder to the module
"""
- property_sheet_id = property_holder.__name__
- context = portal.portal_property_sheets
- if initialize:
- setDefaultClassProperties(property_holder)
+ # Set the module of the given accessor holder properly
+ accessor_holder.__module__ = self.__name__
- try:
- setDefaultProperties(property_holder,
- object=context,
- portal=portal)
- except:
- LOG("Tool.PropertySheetTool", ERROR,
- "Could not generate accessor holder class for %s (module=%s)" % \
- (property_sheet_id, accessor_holder_module_name),
- error=sys.exc_info())
-
- raise
-
- # Create the new accessor holder class and set its module properly
- accessor_holder_class = meta_type(property_sheet_id, (object,), dict(
- __module__ = accessor_holder_module_name,
- constraints = property_holder.constraints,
- # The following attributes have been defined only because they
- # are being used in ERP5Type.Utils when getting all the
- # property_sheets of the property_holder (then, they are added
- # to the local properties, categories and constraints lists)
- _properties = property_holder._properties,
- # Necessary for getBaseCategoryList
- _categories = property_holder._categories,
- _constraints = property_holder._constraints,
- security = property_holder.security
- ))
-
- # Set all the accessors (defined by a tuple) from the Property
- # Holder to the new accessor holder class (code coming from
- # createAccessor in Base.PropertyHolder)
- for id, fake_accessor in property_holder._getPropertyHolderItemList():
- if callable(fake_accessor):
- # not so fake ;)
- setattr(accessor_holder_class, id, fake_accessor)
- continue
- if not isinstance(fake_accessor, tuple):
- continue
-
- if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
- # Case 1 : a workflow method only
- accessor = Base._doNothing
- else:
- # Case 2 : a workflow method over an accessor
- (accessor_class, accessor_args, key) = fake_accessor
- accessor = accessor_class(id, key, *accessor_args)
-
- # Add the accessor to the accessor holder
- setattr(accessor_holder_class, id, accessor)
-
- property_holder.security.apply(accessor_holder_class)
- InitializeClass(accessor_holder_class)
- return accessor_holder_class
+ # Finalize the class as no accessors is added from now on
+ accessor_holder._finalize()
-def _generateBaseAccessorHolder(portal,
- accessor_holder_module):
+ self.__setattr__(accessor_holder.__name__, accessor_holder)
+
+ def clear(self):
+ """
+ Clear the content of the module
+ """
+ for klass in self.__dict__.values():
+ if isinstance(klass, AccessorHolderType):
+ delattr(self, klass.__name__)
+
+def _generateBaseAccessorHolder(portal):
"""
Create once an accessor holder that contains all accessors common to
all portal types: erp5.accessor_holder.BaseAccessorHolder
@@ -142,83 +114,31 @@ def _generateBaseAccessorHolder(portal,
class added to a portal type class, and that it will always be added,
to all living ERP5 objects.
"""
+ import erp5.accessor_holder
+
base_accessor_holder_id = 'BaseAccessorHolder'
- accessor_holder = getattr(accessor_holder_module,
- base_accessor_holder_id,
- None)
- if accessor_holder is not None:
- return accessor_holder
+ try:
+ return getattr(erp5.accessor_holder, base_accessor_holder_id)
+ except AttributeError:
+ # The accessor holder does not already exist
+ pass
# When setting up the site, there will be no portal_categories
- portal_categories = getattr(portal, 'portal_categories', None)
- if portal_categories is None:
+ category_tool = getattr(portal, 'portal_categories', None)
+ if category_tool is None:
return None
- base_category_list = portal_categories.objectIds()
+ base_category_id_list = category_tool.objectIds()
- property_holder = PropertyHolder(base_accessor_holder_id)
+ accessor_holder = AccessorHolderType(base_accessor_holder_id)
- econtext = createExpressionContext(portal_categories, portal)
- createRelatedAccessors(portal_categories,
- property_holder,
- econtext,
- base_category_list)
-
- accessor_holder = AccessorHolderType.fromPropertyHolder(
- property_holder,
- portal,
- 'erp5.accessor_holder',
- initialize=False)
- setattr(accessor_holder_module, base_accessor_holder_id, accessor_holder)
- return accessor_holder
-
-def _generatePreferenceToolAccessorHolder(portal, accessor_holder_list,
- accessor_holder_module):
- """
- Generate a specific Accessor Holder that will be put on the Preference Tool.
- (This used to happen in ERP5Form.PreferenceTool._aq_dynamic)
+ for base_category_id in base_category_id_list:
+ applyCategoryAsRelatedValueAccessor(accessor_holder,
+ base_category_id,
+ category_tool)
- We iterate over all properties that do exist on the system, select the
- preferences out of those, and generate the getPreferred.* accessors.
- """
- property_holder = PropertyHolder('PreferenceTool')
-
- from Products.ERP5Type.Accessor.TypeDefinition import list_types
- from Products.ERP5Type.Utils import convertToUpperCase
- from Products.ERP5Form.PreferenceTool import PreferenceMethod
-
- for accessor_holder in accessor_holder_list:
- for prop in accessor_holder._properties:
- if not prop.get('preference'):
- continue
- # XXX read_permission and write_permissions defined at
- # property sheet are not respected by this.
- # only properties marked as preference are used
-
- # properties have already been 'converted' and _list is appended
- # to list_types properties
- attribute = prop['id']
- if attribute.endswith('_list'):
- attribute = prop['base_id']
- attr_list = [ 'get%s' % convertToUpperCase(attribute)]
- if prop['type'] == 'boolean':
- attr_list.append('is%s' % convertToUpperCase(attribute))
- if prop['type'] in list_types :
- attr_list.append('get%sList' % convertToUpperCase(attribute))
- read_permission = prop.get('read_permission')
- for attribute_name in attr_list:
- method = PreferenceMethod(attribute_name, prop.get('default'))
- setattr(property_holder, attribute_name, method)
- if read_permission:
- property_holder.declareProtected(read_permission, attribute_name)
-
- accessor_holder = AccessorHolderType.fromPropertyHolder(
- property_holder,
- portal,
- 'erp5.accessor_holder',
- initialize=False)
- setattr(accessor_holder_module, 'PreferenceTool', accessor_holder)
+ erp5.accessor_holder.registerAccessorHolder(accessor_holder)
return accessor_holder
related_accessor_definition_dict = {
@@ -417,3 +337,94 @@ def getAccessorHolderList(site, portal_t
# "Created accessor holder for %s" % property_sheet_name)
return accessor_holder_list
+
+from Products.ERP5Type.Base import getClassPropertyList
+
+def createAllAccessorHolderList(site,
+ portal_type_name,
+ portal_type,
+ type_class):
+ """
+ Create the accessor holder list with the given ZODB Property Sheets
+ """
+ from erp5 import accessor_holder as accessor_holder_module
+
+ property_sheet_name_set = set()
+ accessor_holder_list = []
+
+ # Get the accessor holders of the Portal Type
+ if portal_type is not None:
+ accessor_holder_list.extend(portal_type.getAccessorHolderList())
+
+ portal_type_property_sheet_name_set = set(
+ [ accessor_holder.__name__ for accessor_holder in accessor_holder_list ])
+
+ else:
+ portal_type_property_sheet_name_set = set()
+
+ # XXX: Only kept for backward-compatibility as Preference and System
+ # Preference have Preference Type as portal type, which define
+ # getTypePropertySheetList properly and, likewise, Preference Tool
+ # has Preference Tool Type as its portal type
+ if portal_type_name in ("Preference Tool",
+ "Preference",
+ "System Preference"):
+ if portal_type is None or \
+ not portal_type.getPortalType().startswith(portal_type_name):
+ # The Property Sheet Tool may be None if the code is updated but
+ # the BT has not been upgraded yet with portal_property_sheets
+ try:
+ zodb_property_sheet_name_set = set(site.portal_property_sheets.objectIds())
+
+ except AttributeError:
+ if not getattr(site, '_v_bootstrapping', False):
+ LOG("ERP5Type.dynamic", WARNING,
+ "Property Sheet Tool was not found. Please update erp5_core "
+ "Business Template")
+
+ else:
+ for property_sheet in zodb_property_sheet_name_set:
+ if property_sheet.endswith('Preference'):
+ property_sheet_name_set.add(property_sheet)
+
+ # XXX a hook to add per-portal type accessor holders maybe?
+ if portal_type_name == "Preference Tool":
+ from Products.ERP5Form.Document.PreferenceToolType import \
+ _generatePreferenceToolAccessorHolder
+
+ accessor_holder_class = _generatePreferenceToolAccessorHolder(
+ portal_type_name, accessor_holder_list)
+
+ accessor_holder_list.insert(0, accessor_holder_class)
+
+ # Get the Property Sheets defined on the document and its bases
+ # recursively
+ for property_sheet in getClassPropertyList(type_class):
+ # If the Property Sheet is a string, then this is a ZODB
+ # Property Sheet
+ #
+ # NOTE: The Property Sheets of a document should be given as a
+ # string from now on
+ if not isinstance(property_sheet, basestring):
+ property_sheet = property_sheet.__name__
+
+ property_sheet_name_set.add(property_sheet)
+
+ property_sheet_name_set = property_sheet_name_set - \
+ portal_type_property_sheet_name_set
+
+ document_accessor_holder_list = \
+ getAccessorHolderList(site, portal_type_name,
+ getPropertySheetValueList(site,
+ property_sheet_name_set))
+
+ accessor_holder_list.extend(document_accessor_holder_list)
+
+ # useless if Base Category is not yet here or if we're
+ # currently generating accessors for Base Categories
+ accessor_holder_class = _generateBaseAccessorHolder(site)
+
+ if accessor_holder_class is not None:
+ accessor_holder_list.append(accessor_holder_class)
+
+ return accessor_holder_list
Modified: erp5/trunk/products/ERP5Type/dynamic/lazy_class.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/lazy_class.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/lazy_class.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/lazy_class.py [utf8] Wed Mar 2 13:19:02 2011
@@ -7,8 +7,8 @@ from Products.ERP5Type.Accessor.Constant
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Base import Base as ERP5Base
from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods
-from Products.ERP5Type.Utils import createAllCategoryAccessors, \
- createExpressionContext, UpperCase, setDefaultProperties
+from Products.ERP5Type.Utils import UpperCase
+from Products.ERP5Type.Core.CategoryProperty import CategoryProperty
from ExtensionClass import ExtensionClass, pmc_init_of
from zope.interface import classImplements
@@ -130,11 +130,11 @@ class PortalTypeMetaClass(GhostBaseMetaC
cls.security = ClassSecurityInfo()
@classmethod
- def getSubclassList(metacls, cls):
+ def getSubclassList(meta_class, cls):
"""
Returns classes deriving from cls
"""
- return metacls.subclass_register.get(cls, [])
+ return meta_class.subclass_register.get(cls, [])
def getAccessorHolderPropertyList(cls):
"""
@@ -145,10 +145,12 @@ class PortalTypeMetaClass(GhostBaseMetaC
"""
cls.loadClass()
property_dict = {}
+
for klass in cls.mro():
- if klass.__module__ == 'erp5.accessor_holder':
+ if klass.__module__.startswith('erp5.accessor_holder'):
for property in klass._properties:
property_dict.setdefault(property['id'], property)
+
return property_dict.values()
def resetAcquisition(cls):
@@ -210,36 +212,12 @@ class PortalTypeMetaClass(GhostBaseMetaC
raise AttributeError
def generatePortalTypeAccessors(cls, site, portal_type_category_list):
- createAllCategoryAccessors(site,
- cls,
- portal_type_category_list,
- createExpressionContext(site, site))
-
- # Properties defined on the portal type itself are generated in
- # erp5.portal_type directly, but this is unusual case (only
- # PDFTypeInformation seems to use it)
- portal_type_property_list = getattr(cls, '_properties', None)
- if portal_type_property_list:
- setDefaultProperties(cls)
-
- # make sure that category accessors from the portal type definition
- # are generated, no matter what
- # XXX this code is duplicated here, in PropertySheetTool, and in Base
- # and anyway is ugly, as tuple-like registration does not help
- for id, fake_accessor in cls._getPropertyHolderItemList():
- if not isinstance(fake_accessor, tuple):
- continue
-
- if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
- # Case 1 : a workflow method only
- accessor = ERP5Base._doNothing
- else:
- # Case 2 : a workflow method over an accessor
- (accessor_class, accessor_args, key) = fake_accessor
- accessor = accessor_class(id, key, *accessor_args)
-
- # Add the accessor to the accessor holder
- setattr(cls, id, accessor)
+ category_tool = getattr(site, 'portal_categories', None)
+ for category_id in portal_type_category_list:
+ # we need to generate only categories defined on portal type
+ CategoryProperty.applyDefinitionOnAccessorHolder(cls,
+ category_id,
+ category_tool)
portal_workflow = getattr(site, 'portal_workflow', None)
if portal_workflow is None:
@@ -259,9 +237,8 @@ class PortalTypeMetaClass(GhostBaseMetaC
for group in ERP5TypeInformation.defined_group_list:
value = cls.__name__ in site._getPortalGroupedTypeSet(group)
accessor_name = 'is' + UpperCase(group) + 'Type'
- setattr(cls, accessor_name, ConstantGetter(accessor_name, group, value))
- cls.declareProtected(Permissions.AccessContentsInformation,
- accessor_name)
+ method = ConstantGetter(accessor_name, group, value)
+ cls.registerAccessor(method, Permissions.AccessContentsInformation)
from Products.ERP5Type.Cache import initializePortalCachingProperties
initializePortalCachingProperties(site)
@@ -274,7 +251,7 @@ class PortalTypeMetaClass(GhostBaseMetaC
cls.loadClass()
result = PropertyHolder._getPropertyHolderItemList(cls)
for parent in cls.mro():
- if parent.__module__ == 'erp5.accessor_holder':
+ if parent.__module__.startswith('erp5.accessor_holder'):
for x in parent.__dict__.items():
if x[0] not in PropertyHolder.RESERVED_PROPERTY_SET:
result.append(x)
Modified: erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py [utf8] Wed Mar 2 13:19:02 2011
@@ -32,16 +32,14 @@ import os
import inspect
from types import ModuleType
-from dynamic_module import registerDynamicModule
-from accessor_holder import _generateBaseAccessorHolder, \
- _generatePreferenceToolAccessorHolder
-
+from Products.ERP5Type.dynamic.dynamic_module import registerDynamicModule
from Products.ERP5Type.mixin.temporary import TemporaryDocumentMixin
from Products.ERP5Type.Base import Base, resetRegisteredWorkflowMethod
from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import setDefaultClassProperties
from Products.ERP5Type import document_class_registry, mixin_class_registry
-from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
+from Products.ERP5Type.dynamic.accessor_holder import AccessorHolderModuleType, \
+ createAllAccessorHolderList
from zLOG import LOG, ERROR, INFO, WARNING
@@ -59,71 +57,6 @@ def _importClass(classpath):
except StandardError:
raise ImportError('Could not import document class %s' % classpath)
-def _createAccessorHolderList(site,
- portal_type_name,
- property_sheet_name_set):
- """
- Create the accessor holder list with the given ZODB Property Sheets
- """
- from erp5 import accessor_holder
-
- getPropertySheet = site.portal_property_sheets._getOb
- accessor_holder_list = []
-
- if "Base" in property_sheet_name_set:
- # useless if Base Category is not yet here or if we're currently
- # generating accessors for Base Categories
- accessor_holder_class = _generateBaseAccessorHolder(site, accessor_holder)
-
- if accessor_holder_class is not None:
- accessor_holder_list.append(accessor_holder_class)
-
- for property_sheet_name in property_sheet_name_set:
- # LOG("ERP5Type.dynamic", INFO,
- # "Getting accessor holder for " + property_sheet_name)
-
- try:
- # Get the already generated accessor holder
- accessor_holder_list.append(getattr(accessor_holder, property_sheet_name))
-
- except AttributeError:
- try:
- property_sheet = getPropertySheet(property_sheet_name)
- except KeyError:
- LOG("ERP5Type.dynamic", WARNING,
- "Ignoring missing Property Sheet " + property_sheet_name)
-
- continue
-
- # Generate the accessor holder as it has not been done yet
- try:
- accessor_holder_class = property_sheet.createAccessorHolder()
- except Exception:
- LOG("ERP5Type.dynamic", ERROR,
- "Invalid Property Sheet " + property_sheet_name)
- raise
-
- accessor_holder_list.append(accessor_holder_class)
-
- setattr(accessor_holder, property_sheet_name, accessor_holder_class)
-
- # LOG("ERP5Type.dynamic", INFO,
- # "Created accessor holder for %s" % property_sheet_name)
-
- # XXX a hook to add per-portal type accessor holders maybe?
- if portal_type_name == "Preference Tool":
- accessor_holder_class = \
- _generatePreferenceToolAccessorHolder(site,
- accessor_holder_list,
- accessor_holder)
-
- accessor_holder_list.insert(0, accessor_holder_class)
-
- # LOG("ERP5Type.dynamic", INFO,
- # "Got accessor holder for %s: %s" % (property_sheet_name, accessor_holder_list))
-
- return accessor_holder_list
-
# Loading Cache Factory portal type would generate the accessor holder
# for Cache Factory, itself defined with Standard Property thus
# loading the portal type Standard Property, itself defined with
@@ -177,7 +110,6 @@ def generatePortalTypeClass(site, portal
portal_type_category_list = []
attribute_dict = dict(portal_type=portal_type_name,
- _properties=[],
_categories=[],
constraints=[])
@@ -269,80 +201,18 @@ def generatePortalTypeClass(site, portal
property_sheet_generating_portal_type_set.add(portal_type_name)
- property_sheet_tool = getattr(site, 'portal_property_sheets', None)
-
- property_sheet_name_set = set()
-
- # The Property Sheet Tool may be None if the code is updated but
- # the BT has not been upgraded yet with portal_property_sheets
- if property_sheet_tool is None:
- if not getattr(site, '_v_bootstrapping', False):
- LOG("ERP5Type.dynamic", WARNING,
- "Property Sheet Tool was not found. Please update erp5_core "
- "Business Template")
- zodb_property_sheet_name_set = set()
- else:
- zodb_property_sheet_name_set = set(property_sheet_tool.objectIds())
- if portal_type is not None:
- # Get the Property Sheets defined on the portal_type and use the
- # ZODB Property Sheet rather than the filesystem
- for property_sheet in portal_type.getTypePropertySheetList():
- if property_sheet in zodb_property_sheet_name_set:
- property_sheet_name_set.add(property_sheet)
-
- # PDFTypeInformation document class, for example, defines a
- # method which generates dynamically properties and this is
- # heavily used by egov
- update_definition_dict = getattr(portal_type,
- 'updatePropertySheetDefinitionDict',
- None)
-
- if update_definition_dict is not None and not \
- update_definition_dict.__module__.startswith('Products.ERP5Type.ERP5Type'):
- try:
- update_definition_dict(attribute_dict)
- except AttributeError:
- pass
-
- # Only kept for backward-compatibility as Preference and System
- # Preference have Preference Type as portal type, which define
- # getTypePropertySheetList properly and, likewise, Preference Tool
- # has Preference Tool Type as its portal type
- if portal_type_name in ("Preference Tool",
- "Preference",
- "System Preference"):
- if portal_type is None or \
- not portal_type.getPortalType().startswith(portal_type_name):
- for property_sheet in zodb_property_sheet_name_set:
- if property_sheet.endswith('Preference'):
- property_sheet_name_set.add(property_sheet)
-
- # Get the Property Sheets defined on the document and its bases
- # recursively
- from Products.ERP5Type.Base import getClassPropertyList
- for property_sheet in getClassPropertyList(klass):
- # If the Property Sheet is a string, then this is a ZODB
- # Property Sheet
- #
- # NOTE: The Property Sheets of a document should be given as a
- # string from now on
- if not isinstance(property_sheet, basestring):
- property_sheet = property_sheet.__name__
- if property_sheet in zodb_property_sheet_name_set:
- property_sheet_name_set.add(property_sheet)
-
- if property_sheet_name_set:
- # Initialize ZODB Property Sheets accessor holders
- accessor_holder_list = _createAccessorHolderList(site,
+ # Initialize ZODB Property Sheets accessor holders
+ accessor_holder_list = createAllAccessorHolderList(site,
portal_type_name,
- property_sheet_name_set)
+ portal_type,
+ klass)
- base_category_set = set(attribute_dict['_categories'])
- for accessor_holder in accessor_holder_list:
- base_category_set.update(accessor_holder._categories)
- attribute_dict['constraints'].extend(accessor_holder.constraints)
+ base_category_set = set(attribute_dict['_categories'])
+ for accessor_holder in accessor_holder_list:
+ base_category_set.update(accessor_holder._categories)
+ attribute_dict['constraints'].extend(accessor_holder.constraints)
- attribute_dict['_categories'] = list(base_category_set)
+ attribute_dict['_categories'] = list(base_category_set)
property_sheet_generating_portal_type_set.remove(portal_type_name)
@@ -388,15 +258,29 @@ def initializeDynamicModules():
for example classes created through ClassTool that are in
$INSTANCE_HOME/Document
erp5.accessor_holder
- holds accessors of ZODB Property Sheets
+ holds accessor holders common to ZODB Property Sheets and Portal Types
+ erp5.accessor_holder.property_sheet
+ holds accessor holders of ZODB Property Sheets
+ erp5.accessor_holder.portal_type
+ holds accessors holders of Portal Types
"""
erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5
erp5.document = ModuleType("erp5.document")
sys.modules["erp5.document"] = erp5.document
- erp5.accessor_holder = ModuleType("erp5.accessor_holder")
+ erp5.accessor_holder = AccessorHolderModuleType("erp5.accessor_holder")
sys.modules["erp5.accessor_holder"] = erp5.accessor_holder
+ erp5.accessor_holder.property_sheet = \
+ AccessorHolderModuleType("erp5.accessor_holder.property_sheet")
+
+ sys.modules["erp5.accessor_holder.property_sheet"] = \
+ erp5.accessor_holder.property_sheet
+
+ erp5.accessor_holder.portal_type = registerDynamicModule(
+ 'erp5.accessor_holder.portal_type',
+ AccessorHolderModuleType)
+
portal_type_container = registerDynamicModule('erp5.portal_type',
generateLazyPortalTypeClass)
@@ -502,10 +386,14 @@ def synchronizeDynamicModules(context, f
inspect.isclass):
klass.restoreGhostState()
- # Clear accessor holders of ZODB Property Sheets
- for property_sheet_id in erp5.accessor_holder.__dict__.keys():
- if not property_sheet_id.startswith('__'):
- delattr(erp5.accessor_holder, property_sheet_id)
+ # Clear accessor holders of ZODB Property Sheets and Portal Types
+ erp5.accessor_holder.clear()
+ erp5.accessor_holder.property_sheet.clear()
+
+ for name in erp5.accessor_holder.portal_type.__dict__.keys():
+ if name[0] != '_':
+ delattr(erp5.accessor_holder.portal_type, name)
+
finally:
Base.aq_method_lock.release()
Modified: erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] Wed Mar 2 13:19:02 2011
@@ -595,14 +595,16 @@ class TestZodbPropertySheet(ERP5TypeTest
# The accessor holder will be generated once the new Person will
# be created as Person type has test Property Sheet
- self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+ self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
new_person = portal.person_module.newContent(
id='testAssignZodbPropertySheet', portal_type='Person')
- self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
- self.assertTrue(erp5.accessor_holder.TestMigration in \
+ self.assertTrue(erp5.accessor_holder.property_sheet.TestMigration in \
erp5.portal_type.Person.mro())
# Check that the accessors have been properly created for all
@@ -677,7 +679,7 @@ class TestZodbPropertySheet(ERP5TypeTest
new_person = portal.person_module.newContent(
id='testAssignZodbPropertySheet', portal_type='Person')
- self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+ self.failIfHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign')
finally:
@@ -687,15 +689,18 @@ class TestZodbPropertySheet(ERP5TypeTest
def _checkAddPropertyToZodbPropertySheet(self,
new_property_function,
added_accessor_name):
- import erp5.accessor_holder
+ import erp5.accessor_holder.property_sheet
- self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+ self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
new_property_function('add')
self._forceTestAccessorHolderGeneration()
- self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
- self.assertHasAttribute(erp5.accessor_holder.TestMigration,
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
+
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
added_accessor_name)
def testAddStandardPropertyToZodbPropertySheet(self):
@@ -738,15 +743,18 @@ class TestZodbPropertySheet(ERP5TypeTest
change_setter_func,
new_value,
changed_accessor_name):
- import erp5.accessor_holder
+ import erp5.accessor_holder.property_sheet
- self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+ self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
change_setter_func(new_value)
self._forceTestAccessorHolderGeneration()
- self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
- self.assertHasAttribute(erp5.accessor_holder.TestMigration,
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+ 'TestMigration')
+
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
changed_accessor_name)
def testChangeStandardPropertyOfZodbPropertySheet(self):
@@ -798,7 +806,7 @@ class TestZodbPropertySheet(ERP5TypeTest
Delete the given property from the test Property Sheet and check
whether its corresponding accessor is not there anymore
"""
- import erp5.accessor_holder
+ import erp5.accessor_holder.property_sheet
self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
@@ -807,8 +815,8 @@ class TestZodbPropertySheet(ERP5TypeTest
self.test_property_sheet.deleteContent(property_id)
self._forceTestAccessorHolderGeneration()
- self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
- self.failIfHasAttribute(erp5.accessor_holder.TestMigration,
+ self.assertHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
+ self.failIfHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
accessor_name)
def testDeleteStandardPropertyFromZodbPropertySheet(self):
More information about the Erp5-report
mailing list