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)
def registerWorkflowMethod(self, id, wf_id, tr_id, once_per_transaction=0):
portal_type = self.portal_type
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))
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:
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)
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):
......
......@@ -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
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
# Finalize the class as no accessors is added from now on
accessor_holder._finalize()
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)
self.__setattr__(accessor_holder.__name__, accessor_holder)
# 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
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,
accessor_holder_module):
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()
property_holder = PropertyHolder(base_accessor_holder_id)
base_category_id_list = category_tool.objectIds()
econtext = createExpressionContext(portal_categories, portal)
createRelatedAccessors(portal_categories,
property_holder,
econtext,
base_category_list)
accessor_holder = AccessorHolderType(base_accessor_holder_id)
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_type_name, property_sheet_value_list):
# "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
......@@ -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)
......
......@@ -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_type_name):
portal_type_category_list = []
attribute_dict = dict(portal_type=portal_type_name,
_properties=[],
_categories=[],
constraints=[])
......@@ -269,73 +201,11 @@ def generatePortalTypeClass(site, portal_type_name):
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,
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:
......@@ -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, force=False):
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()
......
......@@ -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