Commit 13c22d1a authored by Arnaud Fontaine's avatar Arnaud Fontaine

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.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@43892 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent f38a05c2
...@@ -325,92 +325,21 @@ class PropertyHolder(object): ...@@ -325,92 +325,21 @@ class PropertyHolder(object):
return [x for x in self.__dict__.items() if x[0] not in return [x for x in self.__dict__.items() if x[0] not in
PropertyHolder.RESERVED_PROPERTY_SET] 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): 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)) portal_type = self.portal_type
signature = (wf_id, tr_id, once_per_transaction)
signature_list = self.workflow_method_registry.get(id, ()) workflow_method = getattr(self, id, None)
if signature not in signature_list: if workflow_method is None:
self.workflow_method_registry[id] = signature_list + (signature,) workflow_method = WorkflowMethod(Base._doNothing)
if getattr(self, id, None) is None: setattr(self, id, workflow_method)
setattr(self, id, PropertyHolder.WORKFLOW_METHOD_MARKER) if once_per_transaction:
self.createAccessor(id) 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): def declareProtected(self, permission, accessor_name):
""" """
...@@ -520,23 +449,6 @@ def getClassPropertyList(klass): ...@@ -520,23 +449,6 @@ def getClassPropertyList(klass):
if p not in ps_list]) if p not in ps_list])
return 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): def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
"""We should now make sure workflow methods are defined """We should now make sure workflow methods are defined
and also make sure simulation state is defined.""" and also make sure simulation state is defined."""
...@@ -567,10 +479,8 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow): ...@@ -567,10 +479,8 @@ def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
if not hasattr(ptype_klass, method_id): if not hasattr(ptype_klass, method_id):
method = getter(method_id, wf_id) method = getter(method_id, wf_id)
# Attach to portal_type # Attach to portal_type
setattr(ptype_klass, method_id, method) ptype_klass.registerAccessor(method,
ptype_klass.security.declareProtected( Permissions.AccessContentsInformation)
Permissions.AccessContentsInformation,
method_id )
storage = dc_workflow_dict storage = dc_workflow_dict
transitions = wf.transitions transitions = wf.transitions
...@@ -806,12 +716,6 @@ class Base( CopyContainer, ...@@ -806,12 +716,6 @@ class Base( CopyContainer,
self._setDescription(value) self._setDescription(value)
self.reindexObject() self.reindexObject()
security.declareProtected( Permissions.AccessContentsInformation, 'test_dyn' )
def test_dyn(self):
"""
"""
initializeClassDynamicProperties(self, self.__class__)
security.declarePublic('provides') security.declarePublic('provides')
def provides(cls, interface_name): def provides(cls, interface_name):
""" """
...@@ -848,18 +752,6 @@ class Base( CopyContainer, ...@@ -848,18 +752,6 @@ class Base( CopyContainer,
pformat(rev1.__dict__), pformat(rev1.__dict__),
pformat(rev2.__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): def _aq_dynamic(self, id):
# ahah! disabled, thanks to portal type classes # ahah! disabled, thanks to portal type classes
return None return None
......
...@@ -48,6 +48,7 @@ class PropertySheet(Folder): ...@@ -48,6 +48,7 @@ class PropertySheet(Folder):
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(Permissions.AccessContentsInformation) security.declareObjectProtected(Permissions.AccessContentsInformation)
# TODO: REMOVE
security.declareProtected(Permissions.AccessContentsInformation, security.declareProtected(Permissions.AccessContentsInformation,
'exportToFilesystemDefinition') 'exportToFilesystemDefinition')
def exportToFilesystemDefinition(self): def exportToFilesystemDefinition(self):
...@@ -86,22 +87,15 @@ class PropertySheet(Folder): ...@@ -86,22 +87,15 @@ class PropertySheet(Folder):
return (properties, categories, constraints) return (properties, categories, constraints)
security.declarePrivate('createAccessorHolder') security.declarePrivate('createAccessorHolder')
def createAccessorHolder(self): def createAccessorHolder(self, expression_context, portal):
""" """
Create a new accessor holder from the Property Sheet (the Create a new accessor holder from the Property Sheet
accessors are created through a Property Holder)
""" """
property_holder = PropertyHolder(self.getId()) accessor_holder = AccessorHolderType(self.getId())
# Prepare the Property Holder self.applyOnAccessorHolder(accessor_holder, expression_context, portal)
property_holder._properties, \
property_holder._categories, \
property_holder._constraints = self.exportToFilesystemDefinition()
return AccessorHolderType.fromPropertyHolder( return accessor_holder
property_holder,
self.getPortalObject(),
'erp5.accessor_holder')
@staticmethod @staticmethod
def _guessFilesystemPropertyPortalType(attribute_dict): def _guessFilesystemPropertyPortalType(attribute_dict):
......
This diff is collapsed.
...@@ -7,8 +7,8 @@ from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter ...@@ -7,8 +7,8 @@ from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Base import Base as ERP5Base from Products.ERP5Type.Base import Base as ERP5Base
from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods
from Products.ERP5Type.Utils import createAllCategoryAccessors, \ from Products.ERP5Type.Utils import UpperCase
createExpressionContext, UpperCase, setDefaultProperties from Products.ERP5Type.Core.CategoryProperty import CategoryProperty
from ExtensionClass import ExtensionClass, pmc_init_of from ExtensionClass import ExtensionClass, pmc_init_of
from zope.interface import classImplements from zope.interface import classImplements
...@@ -130,11 +130,11 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -130,11 +130,11 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
cls.security = ClassSecurityInfo() cls.security = ClassSecurityInfo()
@classmethod @classmethod
def getSubclassList(metacls, cls): def getSubclassList(meta_class, cls):
""" """
Returns classes deriving from cls Returns classes deriving from cls
""" """
return metacls.subclass_register.get(cls, []) return meta_class.subclass_register.get(cls, [])
def getAccessorHolderPropertyList(cls): def getAccessorHolderPropertyList(cls):
""" """
...@@ -145,10 +145,12 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -145,10 +145,12 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
""" """
cls.loadClass() cls.loadClass()
property_dict = {} property_dict = {}
for klass in cls.mro(): for klass in cls.mro():
if klass.__module__ == 'erp5.accessor_holder': if klass.__module__.startswith('erp5.accessor_holder'):
for property in klass._properties: for property in klass._properties:
property_dict.setdefault(property['id'], property) property_dict.setdefault(property['id'], property)
return property_dict.values() return property_dict.values()
def resetAcquisition(cls): def resetAcquisition(cls):
...@@ -210,36 +212,12 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -210,36 +212,12 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
raise AttributeError raise AttributeError
def generatePortalTypeAccessors(cls, site, portal_type_category_list): def generatePortalTypeAccessors(cls, site, portal_type_category_list):
createAllCategoryAccessors(site, category_tool = getattr(site, 'portal_categories', None)
cls, for category_id in portal_type_category_list:
portal_type_category_list, # we need to generate only categories defined on portal type
createExpressionContext(site, site)) CategoryProperty.applyDefinitionOnAccessorHolder(cls,
category_id,
# Properties defined on the portal type itself are generated in category_tool)
# 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)
portal_workflow = getattr(site, 'portal_workflow', None) portal_workflow = getattr(site, 'portal_workflow', None)
if portal_workflow is None: if portal_workflow is None:
...@@ -259,9 +237,8 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -259,9 +237,8 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
for group in ERP5TypeInformation.defined_group_list: for group in ERP5TypeInformation.defined_group_list:
value = cls.__name__ in site._getPortalGroupedTypeSet(group) value = cls.__name__ in site._getPortalGroupedTypeSet(group)
accessor_name = 'is' + UpperCase(group) + 'Type' accessor_name = 'is' + UpperCase(group) + 'Type'
setattr(cls, accessor_name, ConstantGetter(accessor_name, group, value)) method = ConstantGetter(accessor_name, group, value)
cls.declareProtected(Permissions.AccessContentsInformation, cls.registerAccessor(method, Permissions.AccessContentsInformation)
accessor_name)
from Products.ERP5Type.Cache import initializePortalCachingProperties from Products.ERP5Type.Cache import initializePortalCachingProperties
initializePortalCachingProperties(site) initializePortalCachingProperties(site)
...@@ -274,7 +251,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder): ...@@ -274,7 +251,7 @@ class PortalTypeMetaClass(GhostBaseMetaClass, PropertyHolder):
cls.loadClass() cls.loadClass()
result = PropertyHolder._getPropertyHolderItemList(cls) result = PropertyHolder._getPropertyHolderItemList(cls)
for parent in cls.mro(): for parent in cls.mro():
if parent.__module__ == 'erp5.accessor_holder': if parent.__module__.startswith('erp5.accessor_holder'):
for x in parent.__dict__.items(): for x in parent.__dict__.items():
if x[0] not in PropertyHolder.RESERVED_PROPERTY_SET: if x[0] not in PropertyHolder.RESERVED_PROPERTY_SET:
result.append(x) result.append(x)
......
...@@ -595,14 +595,16 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -595,14 +595,16 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# The accessor holder will be generated once the new Person will # The accessor holder will be generated once the new Person will
# be created as Person type has test Property Sheet # 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( new_person = portal.person_module.newContent(
id='testAssignZodbPropertySheet', portal_type='Person') 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()) erp5.portal_type.Person.mro())
# Check that the accessors have been properly created for all # Check that the accessors have been properly created for all
...@@ -677,7 +679,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -677,7 +679,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
new_person = portal.person_module.newContent( new_person = portal.person_module.newContent(
id='testAssignZodbPropertySheet', portal_type='Person') id='testAssignZodbPropertySheet', portal_type='Person')
self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign') self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign')
finally: finally:
...@@ -687,15 +689,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -687,15 +689,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
def _checkAddPropertyToZodbPropertySheet(self, def _checkAddPropertyToZodbPropertySheet(self,
new_property_function, new_property_function,
added_accessor_name): 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') new_property_function('add')
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder.property_sheet,
self.assertHasAttribute(erp5.accessor_holder.TestMigration, 'TestMigration')
self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
added_accessor_name) added_accessor_name)
def testAddStandardPropertyToZodbPropertySheet(self): def testAddStandardPropertyToZodbPropertySheet(self):
...@@ -738,15 +743,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -738,15 +743,18 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
change_setter_func, change_setter_func,
new_value, new_value,
changed_accessor_name): 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) change_setter_func(new_value)
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder.property_sheet,
self.assertHasAttribute(erp5.accessor_holder.TestMigration, 'TestMigration')
self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
changed_accessor_name) changed_accessor_name)
def testChangeStandardPropertyOfZodbPropertySheet(self): def testChangeStandardPropertyOfZodbPropertySheet(self):
...@@ -798,7 +806,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -798,7 +806,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
Delete the given property from the test Property Sheet and check Delete the given property from the test Property Sheet and check
whether its corresponding accessor is not there anymore whether its corresponding accessor is not there anymore
""" """
import erp5.accessor_holder import erp5.accessor_holder.property_sheet
self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
...@@ -807,8 +815,8 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -807,8 +815,8 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
self.test_property_sheet.deleteContent(property_id) self.test_property_sheet.deleteContent(property_id)
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
self.failIfHasAttribute(erp5.accessor_holder.TestMigration, self.failIfHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
accessor_name) accessor_name)
def testDeleteStandardPropertyFromZodbPropertySheet(self): def testDeleteStandardPropertyFromZodbPropertySheet(self):
......
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