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):
......
...@@ -33,102 +33,74 @@ Accessor Holders, that is, generation of methods for ERP5 ...@@ -33,102 +33,74 @@ Accessor Holders, that is, generation of methods for ERP5
* Utils, Property Sheet Tool can be probably be cleaned up as well by * Utils, Property Sheet Tool can be probably be cleaned up as well by
moving specialized code here. moving specialized code here.
""" """
import sys from types import ModuleType
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from Products.ERP5Type.Base import PropertyHolder, Base from Products.ERP5Type.Utils import createExpressionContext
from Products.ERP5Type.Utils import createRelatedAccessors, createExpressionContext
from Products.ERP5Type.Utils import setDefaultClassProperties, setDefaultProperties
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import UpperCase from Products.ERP5Type.Utils import UpperCase
from Products.ERP5Type.Accessor import Related, RelatedValue 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): class AccessorHolderType(type):
_skip_permission_tuple = (Permissions.AccessContentsInformation, _skip_permission_tuple = (Permissions.AccessContentsInformation,
Permissions.ModifyPortalContent) Permissions.ModifyPortalContent)
def _next_registerAccessor(cls, def registerAccessor(cls,
accessor, accessor,
permission): permission=None):
accessor_name = accessor.__name__ accessor_name = accessor.__name__
setattr(cls, accessor_name, accessor) setattr(cls, accessor_name, accessor)
if permission is None:
return
# private accessors do not need declarative security # private accessors do not need declarative security
if accessor_name[0] != '_' and \ if accessor_name[0] != '_' and \
permission not in AccessorHolderType._skip_permission_tuple: permission not in AccessorHolderType._skip_permission_tuple:
cls.security.declareProtected(permission, accessor_name) cls.security.declareProtected(permission, accessor_name)
@classmethod def __new__(meta_class, class_name, base_tuple=(object,), attribute_dict={}):
def fromPropertyHolder(meta_type, # we dont want to add several times to the same list, so make sure
property_holder, # that duplicate attributes just point to the same list object
portal=None, constraint_list = []
accessor_holder_module_name=None, attribute_dict.update(_categories=[],
initialize=True): _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 Add an accessor holder to the module
within the given accessor holder module
""" """
property_sheet_id = property_holder.__name__ # Set the module of the given accessor holder properly
context = portal.portal_property_sheets accessor_holder.__module__ = self.__name__
if initialize:
setDefaultClassProperties(property_holder)
try: # Finalize the class as no accessors is added from now on
setDefaultProperties(property_holder, accessor_holder._finalize()
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 self.__setattr__(accessor_holder.__name__, accessor_holder)
setattr(accessor_holder_class, id, accessor)
property_holder.security.apply(accessor_holder_class) def clear(self):
InitializeClass(accessor_holder_class) """
return accessor_holder_class Clear the content of the module
"""
for klass in self.__dict__.values():
if isinstance(klass, AccessorHolderType):
delattr(self, klass.__name__)
def _generateBaseAccessorHolder(portal, def _generateBaseAccessorHolder(portal):
accessor_holder_module):
""" """
Create once an accessor holder that contains all accessors common to Create once an accessor holder that contains all accessors common to
all portal types: erp5.accessor_holder.BaseAccessorHolder all portal types: erp5.accessor_holder.BaseAccessorHolder
...@@ -142,83 +114,31 @@ def _generateBaseAccessorHolder(portal, ...@@ -142,83 +114,31 @@ def _generateBaseAccessorHolder(portal,
class added to a portal type class, and that it will always be added, class added to a portal type class, and that it will always be added,
to all living ERP5 objects. to all living ERP5 objects.
""" """
import erp5.accessor_holder
base_accessor_holder_id = 'BaseAccessorHolder' base_accessor_holder_id = 'BaseAccessorHolder'
accessor_holder = getattr(accessor_holder_module, try:
base_accessor_holder_id, return getattr(erp5.accessor_holder, base_accessor_holder_id)
None) except AttributeError:
if accessor_holder is not None: # The accessor holder does not already exist
return accessor_holder pass
# When setting up the site, there will be no portal_categories # When setting up the site, there will be no portal_categories
portal_categories = getattr(portal, 'portal_categories', None) category_tool = getattr(portal, 'portal_categories', None)
if portal_categories is None: if category_tool is None:
return 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) for base_category_id in base_category_id_list:
createRelatedAccessors(portal_categories, applyCategoryAsRelatedValueAccessor(accessor_holder,
property_holder, base_category_id,
econtext, category_tool)
base_category_list)
accessor_holder = AccessorHolderType.fromPropertyHolder( erp5.accessor_holder.registerAccessorHolder(accessor_holder)
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)
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)
return accessor_holder return accessor_holder
related_accessor_definition_dict = { related_accessor_definition_dict = {
...@@ -417,3 +337,94 @@ def getAccessorHolderList(site, portal_type_name, property_sheet_value_list): ...@@ -417,3 +337,94 @@ def getAccessorHolderList(site, portal_type_name, property_sheet_value_list):
# "Created accessor holder for %s" % property_sheet_name) # "Created accessor holder for %s" % property_sheet_name)
return accessor_holder_list 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
...@@ -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)
......
...@@ -32,16 +32,14 @@ import os ...@@ -32,16 +32,14 @@ import os
import inspect import inspect
from types import ModuleType from types import ModuleType
from dynamic_module import registerDynamicModule from Products.ERP5Type.dynamic.dynamic_module import registerDynamicModule
from accessor_holder import _generateBaseAccessorHolder, \
_generatePreferenceToolAccessorHolder
from Products.ERP5Type.mixin.temporary import TemporaryDocumentMixin from Products.ERP5Type.mixin.temporary import TemporaryDocumentMixin
from Products.ERP5Type.Base import Base, resetRegisteredWorkflowMethod from Products.ERP5Type.Base import Base, resetRegisteredWorkflowMethod
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import setDefaultClassProperties from Products.ERP5Type.Utils import setDefaultClassProperties
from Products.ERP5Type import document_class_registry, mixin_class_registry 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 from zLOG import LOG, ERROR, INFO, WARNING
...@@ -59,71 +57,6 @@ def _importClass(classpath): ...@@ -59,71 +57,6 @@ def _importClass(classpath):
except StandardError: except StandardError:
raise ImportError('Could not import document class %s' % classpath) 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 # Loading Cache Factory portal type would generate the accessor holder
# for Cache Factory, itself defined with Standard Property thus # for Cache Factory, itself defined with Standard Property thus
# loading the portal type Standard Property, itself defined with # loading the portal type Standard Property, itself defined with
...@@ -177,7 +110,6 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -177,7 +110,6 @@ def generatePortalTypeClass(site, portal_type_name):
portal_type_category_list = [] portal_type_category_list = []
attribute_dict = dict(portal_type=portal_type_name, attribute_dict = dict(portal_type=portal_type_name,
_properties=[],
_categories=[], _categories=[],
constraints=[]) constraints=[])
...@@ -269,80 +201,18 @@ def generatePortalTypeClass(site, portal_type_name): ...@@ -269,80 +201,18 @@ def generatePortalTypeClass(site, portal_type_name):
property_sheet_generating_portal_type_set.add(portal_type_name) property_sheet_generating_portal_type_set.add(portal_type_name)
property_sheet_tool = getattr(site, 'portal_property_sheets', None) # Initialize ZODB Property Sheets accessor holders
accessor_holder_list = createAllAccessorHolderList(site,
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,
portal_type_name, portal_type_name,
property_sheet_name_set) portal_type,
klass)
base_category_set = set(attribute_dict['_categories']) base_category_set = set(attribute_dict['_categories'])
for accessor_holder in accessor_holder_list: for accessor_holder in accessor_holder_list:
base_category_set.update(accessor_holder._categories) base_category_set.update(accessor_holder._categories)
attribute_dict['constraints'].extend(accessor_holder.constraints) 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) property_sheet_generating_portal_type_set.remove(portal_type_name)
...@@ -388,15 +258,29 @@ def initializeDynamicModules(): ...@@ -388,15 +258,29 @@ def initializeDynamicModules():
for example classes created through ClassTool that are in for example classes created through ClassTool that are in
$INSTANCE_HOME/Document $INSTANCE_HOME/Document
erp5.accessor_holder 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") erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5 sys.modules["erp5"] = erp5
erp5.document = ModuleType("erp5.document") erp5.document = ModuleType("erp5.document")
sys.modules["erp5.document"] = 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 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', portal_type_container = registerDynamicModule('erp5.portal_type',
generateLazyPortalTypeClass) generateLazyPortalTypeClass)
...@@ -502,10 +386,14 @@ def synchronizeDynamicModules(context, force=False): ...@@ -502,10 +386,14 @@ def synchronizeDynamicModules(context, force=False):
inspect.isclass): inspect.isclass):
klass.restoreGhostState() klass.restoreGhostState()
# Clear accessor holders of ZODB Property Sheets # Clear accessor holders of ZODB Property Sheets and Portal Types
for property_sheet_id in erp5.accessor_holder.__dict__.keys(): erp5.accessor_holder.clear()
if not property_sheet_id.startswith('__'): erp5.accessor_holder.property_sheet.clear()
delattr(erp5.accessor_holder, property_sheet_id)
for name in erp5.accessor_holder.portal_type.__dict__.keys():
if name[0] != '_':
delattr(erp5.accessor_holder.portal_type, name)
finally: finally:
Base.aq_method_lock.release() Base.aq_method_lock.release()
......
...@@ -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