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):
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 initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
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
......
......@@ -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()
self.applyOnAccessorHolder(accessor_holder, expression_context, portal)
return AccessorHolderType.fromPropertyHolder(
property_holder,
self.getPortalObject(),
'erp5.accessor_holder')
return accessor_holder
@staticmethod
def _guessFilesystemPropertyPortalType(attribute_dict):
......
This diff is collapsed.
......@@ -7,8 +7,8 @@ from Products.ERP5Type.Accessor.Constant import Getter as ConstantGetter
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(GhostBaseMetaClass, PropertyHolder):
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(GhostBaseMetaClass, PropertyHolder):
"""
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(GhostBaseMetaClass, PropertyHolder):
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(GhostBaseMetaClass, PropertyHolder):
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(GhostBaseMetaClass, PropertyHolder):
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)
......
......@@ -595,14 +595,16 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# 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(ERP5TypeTestCase):
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(ERP5TypeTestCase):
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(ERP5TypeTestCase):
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(ERP5TypeTestCase):
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(ERP5TypeTestCase):
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):
......
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