[Erp5-report] r43892 arnaud.fontaine - in /erp5/trunk/products/ERP5Type: ./ Core/ dynamic/ ...

nobody at svn.erp5.org nobody at svn.erp5.org
Wed Mar 2 13:19:02 CET 2011


Author: arnaud.fontaine
Date: Wed Mar  2 13:19:02 2011
New Revision: 43892

URL: http://svn.erp5.org?rev=43892&view=rev
Log:
Accessors generation is now performed in StandardProperty, AcquiredProperty, 
CategoryProperty and DynamicCategoryProperty rather than setDefaultProperties
from Utils.

erp5.accessor_holder has also been split up into two additional modules, namely 
erp5.accessor_holder.property_sheet, containing accessor holders for ZODB 
Property Sheets, and erp5.accessor_holder.portal_type, containing accessor 
holders specific to the Portal Types (only being used by PreferenceTool and egov 
for now). erp5.accessor_holder only contains accessor holders common to both 
Portal Types and Property Sheets (such as BaseAccessorHolder).

This commit also enables code committed in r43886.

Modified:
    erp5/trunk/products/ERP5Type/Base.py
    erp5/trunk/products/ERP5Type/Core/PropertySheet.py
    erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py
    erp5/trunk/products/ERP5Type/dynamic/lazy_class.py
    erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py
    erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py

Modified: erp5/trunk/products/ERP5Type/Base.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Base.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Base.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Base.py [utf8] Wed Mar  2 13:19:02 2011
@@ -325,92 +325,21 @@ class PropertyHolder(object):
     return [x for x in self.__dict__.items() if x[0] not in
         PropertyHolder.RESERVED_PROPERTY_SET]
 
-  # Accessor generation
-  def createAccessor(self, id):
-    """
-    Invokes appropriate factory and create an accessor
-    """
-    fake_accessor = getattr(self, id)
-    ptype = getattr(self, 'portal_type', None)
-    if ptype is None:
-      ptype = self._portal_type
-    if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
-      # Case 1 : a workflow method only
-      accessor = Base._doNothing
-    else:
-      # Case 2 : a workflow method over an accessor
-      (accessor_class, accessor_args, key) = fake_accessor
-      accessor = accessor_class(id, key, *accessor_args)
-    for wf_id, tr_id, once in self.workflow_method_registry.get(id, ()):
-      if not isinstance(accessor, WorkflowMethod):
-        accessor = WorkflowMethod(accessor)
-        if once:
-          accessor.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
-        else:
-          accessor.registerTransitionAlways(ptype, wf_id, tr_id)
-      else:
-        if once:
-          accessor.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
-        else:
-          accessor.registerTransitionAlways(ptype, wf_id, tr_id)
-    setattr(self, id, accessor)
-    return accessor
-
-  def registerAccessor(self, id, key, accessor_class, accessor_args):
-    """
-    Saves in a dictionary all parameters required to create an accessor
-    The goal here is to minimize memory occupation. We have found the following:
-
-    - the size of a tuple with simple types and the size
-      of None are the same (a pointer)
-
-    - the size of a pointer to a class is the same as the
-      size of None
-
-    - the python caching system for tuples is efficient for tuples
-      which contain simple types (string, int, etc.) but innefficient
-      for tuples which contain a pointer
-
-    - as a result, it is better to create separate dicts if
-      values contain pointers and single dict if value is
-      a tuple of simple types
-
-    Parameters:
-
-    id  --  The id the accessor (ex. getFirstName)
-
-    key --  The id of the property (ex. first_name) or the id of the
-            method for Alias accessors
-    """
-    #LOG('registerAccessor', 0, "%s %s %s" % (id , self._portal_type, accessor_args))
-    # First we try to compress the information required
-    # to build a new accessor in such way that
-    # if the same information is provided twice, we
-    # shall keep it once only
-    new_accessor_args = []
-    for arg in accessor_args:
-      if type(arg) is types.ListType:
-        new_accessor_args.append(tuple(arg))
-      else:
-        new_accessor_args.append(arg)
-    accessor_args = tuple(new_accessor_args)
-    original_registration_tuple = (accessor_class, accessor_args, key)
-    registration_tuple = method_registration_cache.get(original_registration_tuple)
-    if registration_tuple is None:
-      registration_tuple = original_registration_tuple
-      method_registration_cache[registration_tuple] = registration_tuple
-    # Use the cached tuple (same value, different pointer)
-    setattr(self, id, registration_tuple)
-
   def registerWorkflowMethod(self, id, wf_id, tr_id, once_per_transaction=0):
-    #LOG('registerWorkflowMethod', 0, "%s %s %s %s %s" % (self._portal_type, id, wf_id, tr_id, once_per_transaction))
-    signature = (wf_id, tr_id, once_per_transaction)
-    signature_list = self.workflow_method_registry.get(id, ())
-    if signature not in signature_list:
-      self.workflow_method_registry[id] = signature_list + (signature,)
-    if getattr(self, id, None) is None:
-      setattr(self, id, PropertyHolder.WORKFLOW_METHOD_MARKER)
-      self.createAccessor(id)
+    portal_type = self.portal_type
+
+    workflow_method = getattr(self, id, None)
+    if workflow_method is None:
+      workflow_method = WorkflowMethod(Base._doNothing)
+      setattr(self, id, workflow_method)
+    if once_per_transaction:
+      workflow_method.registerTransitionOncePerTransaction(portal_type,
+                                                           wf_id,
+                                                           tr_id)
+    else:
+      workflow_method.registerTransitionAlways(portal_type,
+                                               wf_id,
+                                               tr_id)
 
   def declareProtected(self, permission, accessor_name):
     """
@@ -520,23 +449,6 @@ def getClassPropertyList(klass):
         if p not in ps_list])
   return ps_list
 
-def initializeClassDynamicProperties(self, klass):
-  if klass not in Base.aq_method_generated:
-    # Recurse to superclasses
-    for super_klass in klass.__bases__:
-      if getattr(super_klass, 'isRADContent', 0):
-        initializeClassDynamicProperties(self, super_klass)
-    # Initialize default properties
-    from Utils import setDefaultClassProperties
-    if not getattr(klass, 'isPortalContent', None):
-      if getattr(klass, 'isRADContent', 0):
-        setDefaultClassProperties(klass)
-      # Mark as generated
-      Base.aq_method_generated.add(klass)
-
-def initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal):
-  raise ValueError("No reason to go through this no more with portal type classes")
-
 def initializePortalTypeDynamicWorkflowMethods(ptype_klass, portal_workflow):
   """We should now make sure workflow methods are defined
   and also make sure simulation state is defined."""
@@ -567,10 +479,8 @@ def initializePortalTypeDynamicWorkflowM
         if not hasattr(ptype_klass, method_id):
           method = getter(method_id, wf_id)
           # Attach to portal_type
-          setattr(ptype_klass, method_id, method)
-          ptype_klass.security.declareProtected(
-                                 Permissions.AccessContentsInformation,
-                                 method_id )
+          ptype_klass.registerAccessor(method,
+                                       Permissions.AccessContentsInformation)
 
       storage = dc_workflow_dict
       transitions = wf.transitions
@@ -806,12 +716,6 @@ class Base( CopyContainer,
     self._setDescription(value)
     self.reindexObject()
 
-  security.declareProtected( Permissions.AccessContentsInformation, 'test_dyn' )
-  def test_dyn(self):
-    """
-    """
-    initializeClassDynamicProperties(self, self.__class__)
-
   security.declarePublic('provides')
   def provides(cls, interface_name):
     """
@@ -848,18 +752,6 @@ class Base( CopyContainer,
               pformat(rev1.__dict__),
               pformat(rev2.__dict__)))
 
-  def initializePortalTypeDynamicProperties(self):
-    """
-      Test purpose
-    """
-    ptype = self.portal_type
-    klass = self.__class__
-    aq_key = self._aq_key()
-    initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, \
-        self.getPortalObject())
-    from Products.ERP5Form.PreferenceTool import createPreferenceToolAccessorList
-    createPreferenceToolAccessorList(self.getPortalObject())
-
   def _aq_dynamic(self, id):
     # ahah! disabled, thanks to portal type classes
     return None

Modified: erp5/trunk/products/ERP5Type/Core/PropertySheet.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Core/PropertySheet.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Core/PropertySheet.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Core/PropertySheet.py [utf8] Wed Mar  2 13:19:02 2011
@@ -48,6 +48,7 @@ class PropertySheet(Folder):
   security = ClassSecurityInfo()
   security.declareObjectProtected(Permissions.AccessContentsInformation)
 
+  # TODO: REMOVE
   security.declareProtected(Permissions.AccessContentsInformation,
                             'exportToFilesystemDefinition')
   def exportToFilesystemDefinition(self):
@@ -86,22 +87,15 @@ class PropertySheet(Folder):
     return (properties, categories, constraints)
 
   security.declarePrivate('createAccessorHolder')
-  def createAccessorHolder(self):
+  def createAccessorHolder(self, expression_context, portal):
     """
-    Create a new accessor holder from the Property Sheet (the
-    accessors are created through a Property Holder)
+    Create a new accessor holder from the Property Sheet
     """
-    property_holder = PropertyHolder(self.getId())
+    accessor_holder = AccessorHolderType(self.getId())
 
-    # Prepare the Property Holder
-    property_holder._properties, \
-      property_holder._categories, \
-      property_holder._constraints = self.exportToFilesystemDefinition()
-
-    return AccessorHolderType.fromPropertyHolder(
-      property_holder,
-      self.getPortalObject(),
-      'erp5.accessor_holder')
+    self.applyOnAccessorHolder(accessor_holder, expression_context, portal)
+
+    return accessor_holder
 
   @staticmethod
   def _guessFilesystemPropertyPortalType(attribute_dict):

Modified: erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/accessor_holder.py [utf8] Wed Mar  2 13:19:02 2011
@@ -33,102 +33,74 @@ Accessor Holders, that is, generation of
 * Utils, Property Sheet Tool can be probably be cleaned up as well by
 moving specialized code here.
 """
-import sys
+from types import ModuleType
 
 from Products.ERP5Type import Permissions
-from Products.ERP5Type.Base import PropertyHolder, Base
-from Products.ERP5Type.Utils import createRelatedAccessors, createExpressionContext
-from Products.ERP5Type.Utils import setDefaultClassProperties, setDefaultProperties
+from Products.ERP5Type.Utils import createExpressionContext
 from Products.ERP5Type.Globals import InitializeClass
 
 from Products.ERP5Type.Utils import UpperCase
 from Products.ERP5Type.Accessor import Related, RelatedValue
+from AccessControl import ClassSecurityInfo
 
-from zLOG import LOG, ERROR, INFO
+from zLOG import LOG, ERROR, INFO, WARNING
 
 class AccessorHolderType(type):
   _skip_permission_tuple = (Permissions.AccessContentsInformation,
                             Permissions.ModifyPortalContent)
-  def _next_registerAccessor(cls,
+  def registerAccessor(cls,
                        accessor,
-                       permission):
+                       permission=None):
     accessor_name = accessor.__name__
     setattr(cls, accessor_name, accessor)
+    if permission is None:
+      return
     # private accessors do not need declarative security
     if accessor_name[0] != '_' and \
         permission not in AccessorHolderType._skip_permission_tuple:
       cls.security.declareProtected(permission, accessor_name)
 
-  @classmethod
-  def fromPropertyHolder(meta_type,
-                         property_holder,
-                         portal=None,
-                         accessor_holder_module_name=None,
-                         initialize=True):
+  def __new__(meta_class, class_name, base_tuple=(object,), attribute_dict={}):
+    # we dont want to add several times to the same list, so make sure
+    # that duplicate attributes just point to the same list object
+    constraint_list = []
+    attribute_dict.update(_categories=[],
+                          _constraints=constraint_list,
+                          constraints=constraint_list,
+                          security=ClassSecurityInfo(),
+                          _properties=[])
+
+    return super(AccessorHolderType, meta_class).__new__(meta_class,
+                                                         class_name,
+                                                         base_tuple,
+                                                         attribute_dict)
+
+  def _finalize(cls):
+    cls.security.apply(cls)
+    InitializeClass(cls)
+
+class AccessorHolderModuleType(ModuleType):
+  def registerAccessorHolder(self, accessor_holder):
     """
-    Create a new accessor holder class from the given Property Holder
-    within the given accessor holder module
+    Add an accessor holder to the module
     """
-    property_sheet_id = property_holder.__name__
-    context = portal.portal_property_sheets
-    if initialize:
-      setDefaultClassProperties(property_holder)
+    # Set the module of the given accessor holder properly
+    accessor_holder.__module__ = self.__name__
 
-      try:
-        setDefaultProperties(property_holder,
-                             object=context,
-                             portal=portal)
-      except:
-        LOG("Tool.PropertySheetTool", ERROR,
-            "Could not generate accessor holder class for %s (module=%s)" % \
-            (property_sheet_id, accessor_holder_module_name),
-            error=sys.exc_info())
-
-        raise
-
-    # Create the new accessor holder class and set its module properly
-    accessor_holder_class = meta_type(property_sheet_id, (object,), dict(
-      __module__ = accessor_holder_module_name,
-      constraints = property_holder.constraints,
-      # The following attributes have been defined only because they
-      # are being used in ERP5Type.Utils when getting all the
-      # property_sheets of the property_holder (then, they are added
-      # to the local properties, categories and constraints lists)
-      _properties = property_holder._properties,
-      # Necessary for getBaseCategoryList
-      _categories = property_holder._categories,
-      _constraints = property_holder._constraints,
-      security = property_holder.security
-      ))
-
-    # Set all the accessors (defined by a tuple) from the Property
-    # Holder to the new accessor holder class (code coming from
-    # createAccessor in Base.PropertyHolder)
-    for id, fake_accessor in property_holder._getPropertyHolderItemList():
-      if callable(fake_accessor):
-        # not so fake ;)
-        setattr(accessor_holder_class, id, fake_accessor)
-        continue
-      if not isinstance(fake_accessor, tuple):
-        continue
-
-      if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
-        # Case 1 : a workflow method only
-        accessor = Base._doNothing
-      else:
-        # Case 2 : a workflow method over an accessor
-        (accessor_class, accessor_args, key) = fake_accessor
-        accessor = accessor_class(id, key, *accessor_args)
-
-      # Add the accessor to the accessor holder
-      setattr(accessor_holder_class, id, accessor)
-
-    property_holder.security.apply(accessor_holder_class)
-    InitializeClass(accessor_holder_class)
-    return accessor_holder_class
+    # Finalize the class as no accessors is added from now on
+    accessor_holder._finalize()
 
-def _generateBaseAccessorHolder(portal,
-                                accessor_holder_module):
+    self.__setattr__(accessor_holder.__name__, accessor_holder)
+
+  def clear(self):
+    """
+    Clear the content of the module
+    """
+    for klass in self.__dict__.values():
+      if isinstance(klass, AccessorHolderType):
+        delattr(self, klass.__name__)
+
+def _generateBaseAccessorHolder(portal):
   """
   Create once an accessor holder that contains all accessors common to
   all portal types: erp5.accessor_holder.BaseAccessorHolder
@@ -142,83 +114,31 @@ def _generateBaseAccessorHolder(portal,
   class added to a portal type class, and that it will always be added,
   to all living ERP5 objects.
   """
+  import erp5.accessor_holder
+
   base_accessor_holder_id = 'BaseAccessorHolder'
 
-  accessor_holder = getattr(accessor_holder_module,
-                            base_accessor_holder_id,
-                            None)
-  if accessor_holder is not None:
-    return accessor_holder
+  try:
+    return getattr(erp5.accessor_holder, base_accessor_holder_id)
+  except AttributeError:
+    # The accessor holder does not already exist
+    pass
 
   # When setting up the site, there will be no portal_categories
-  portal_categories = getattr(portal, 'portal_categories', None)
-  if portal_categories is None:
+  category_tool = getattr(portal, 'portal_categories', None)
+  if category_tool is None:
     return None
 
-  base_category_list = portal_categories.objectIds()
+  base_category_id_list = category_tool.objectIds()
 
-  property_holder = PropertyHolder(base_accessor_holder_id)
+  accessor_holder = AccessorHolderType(base_accessor_holder_id)
 
-  econtext = createExpressionContext(portal_categories, portal)
-  createRelatedAccessors(portal_categories,
-                         property_holder,
-                         econtext,
-                         base_category_list)
-
-  accessor_holder = AccessorHolderType.fromPropertyHolder(
-                      property_holder,
-                      portal,
-                      'erp5.accessor_holder',
-                      initialize=False)
-  setattr(accessor_holder_module, base_accessor_holder_id, accessor_holder)
-  return accessor_holder
-
-def _generatePreferenceToolAccessorHolder(portal, accessor_holder_list,
-    accessor_holder_module):
-  """
-  Generate a specific Accessor Holder that will be put on the Preference Tool.
-  (This used to happen in ERP5Form.PreferenceTool._aq_dynamic)
+  for base_category_id in base_category_id_list:
+    applyCategoryAsRelatedValueAccessor(accessor_holder,
+                                        base_category_id,
+                                        category_tool)
 
-  We iterate over all properties that do exist on the system, select the
-  preferences out of those, and generate the getPreferred.* accessors.
-  """
-  property_holder = PropertyHolder('PreferenceTool')
-
-  from Products.ERP5Type.Accessor.TypeDefinition import list_types
-  from Products.ERP5Type.Utils import convertToUpperCase
-  from Products.ERP5Form.PreferenceTool import PreferenceMethod
-
-  for accessor_holder in accessor_holder_list:
-    for prop in accessor_holder._properties:
-      if not prop.get('preference'):
-        continue
-      # XXX read_permission and write_permissions defined at
-      # property sheet are not respected by this.
-      # only properties marked as preference are used
-
-      # properties have already been 'converted' and _list is appended
-      # to list_types properties
-      attribute = prop['id']
-      if attribute.endswith('_list'):
-        attribute = prop['base_id']
-      attr_list = [ 'get%s' % convertToUpperCase(attribute)]
-      if prop['type'] == 'boolean':
-        attr_list.append('is%s' % convertToUpperCase(attribute))
-      if prop['type'] in list_types :
-        attr_list.append('get%sList' % convertToUpperCase(attribute))
-      read_permission = prop.get('read_permission')
-      for attribute_name in attr_list:
-        method = PreferenceMethod(attribute_name, prop.get('default'))
-        setattr(property_holder, attribute_name, method)
-        if read_permission:
-          property_holder.declareProtected(read_permission, attribute_name)
-
-  accessor_holder = AccessorHolderType.fromPropertyHolder(
-                      property_holder,
-                      portal,
-                      'erp5.accessor_holder',
-                      initialize=False)
-  setattr(accessor_holder_module, 'PreferenceTool', accessor_holder)
+  erp5.accessor_holder.registerAccessorHolder(accessor_holder)
   return accessor_holder
 
 related_accessor_definition_dict = {
@@ -417,3 +337,94 @@ def getAccessorHolderList(site, portal_t
       #     "Created accessor holder for %s" % property_sheet_name)
 
   return accessor_holder_list
+
+from Products.ERP5Type.Base import getClassPropertyList
+
+def createAllAccessorHolderList(site,
+                                portal_type_name,
+                                portal_type,
+                                type_class):
+  """
+  Create the accessor holder list with the given ZODB Property Sheets
+  """
+  from erp5 import accessor_holder as accessor_holder_module
+
+  property_sheet_name_set = set()
+  accessor_holder_list = []
+
+  # Get the accessor holders of the Portal Type
+  if portal_type is not None:
+    accessor_holder_list.extend(portal_type.getAccessorHolderList())
+
+    portal_type_property_sheet_name_set = set(
+      [ accessor_holder.__name__ for accessor_holder in accessor_holder_list ])
+
+  else:
+    portal_type_property_sheet_name_set = set()
+
+  # XXX: Only kept for backward-compatibility as Preference and System
+  # Preference have Preference Type as portal type, which define
+  # getTypePropertySheetList properly and, likewise, Preference Tool
+  # has Preference Tool Type as its portal type
+  if portal_type_name in ("Preference Tool",
+                          "Preference",
+                          "System Preference"):
+    if portal_type is None or \
+       not portal_type.getPortalType().startswith(portal_type_name):
+      # The Property Sheet Tool may be None if the code is updated but
+      # the BT has not been upgraded yet with portal_property_sheets
+      try:
+        zodb_property_sheet_name_set = set(site.portal_property_sheets.objectIds())
+
+      except AttributeError:
+        if not getattr(site, '_v_bootstrapping', False):
+          LOG("ERP5Type.dynamic", WARNING,
+              "Property Sheet Tool was not found. Please update erp5_core "
+              "Business Template")
+
+      else:
+        for property_sheet in zodb_property_sheet_name_set:
+          if property_sheet.endswith('Preference'):
+            property_sheet_name_set.add(property_sheet)
+
+      # XXX a hook to add per-portal type accessor holders maybe?
+      if portal_type_name == "Preference Tool":
+        from Products.ERP5Form.Document.PreferenceToolType import \
+            _generatePreferenceToolAccessorHolder
+
+        accessor_holder_class = _generatePreferenceToolAccessorHolder(
+          portal_type_name, accessor_holder_list)
+
+        accessor_holder_list.insert(0, accessor_holder_class)
+
+  # Get the Property Sheets defined on the document and its bases
+  # recursively
+  for property_sheet in getClassPropertyList(type_class):
+    # If the Property Sheet is a string, then this is a ZODB
+    # Property Sheet
+    #
+    # NOTE: The Property Sheets of a document should be given as a
+    #       string from now on
+    if not isinstance(property_sheet, basestring):
+      property_sheet = property_sheet.__name__
+
+    property_sheet_name_set.add(property_sheet)
+
+  property_sheet_name_set = property_sheet_name_set - \
+      portal_type_property_sheet_name_set
+
+  document_accessor_holder_list = \
+      getAccessorHolderList(site, portal_type_name,
+                            getPropertySheetValueList(site,
+                                                      property_sheet_name_set))
+
+  accessor_holder_list.extend(document_accessor_holder_list)
+
+  # useless if Base Category is not yet here or if we're
+  # currently generating accessors for Base Categories
+  accessor_holder_class = _generateBaseAccessorHolder(site)
+
+  if accessor_holder_class is not None:
+    accessor_holder_list.append(accessor_holder_class)
+
+  return accessor_holder_list

Modified: erp5/trunk/products/ERP5Type/dynamic/lazy_class.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/lazy_class.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/lazy_class.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/lazy_class.py [utf8] Wed Mar  2 13:19:02 2011
@@ -7,8 +7,8 @@ from Products.ERP5Type.Accessor.Constant
 from Products.ERP5Type.Globals import InitializeClass
 from Products.ERP5Type.Base import Base as ERP5Base
 from Products.ERP5Type.Base import PropertyHolder, initializePortalTypeDynamicWorkflowMethods
-from Products.ERP5Type.Utils import createAllCategoryAccessors, \
-    createExpressionContext, UpperCase, setDefaultProperties
+from Products.ERP5Type.Utils import UpperCase
+from Products.ERP5Type.Core.CategoryProperty import CategoryProperty
 from ExtensionClass import ExtensionClass, pmc_init_of
 
 from zope.interface import classImplements
@@ -130,11 +130,11 @@ class PortalTypeMetaClass(GhostBaseMetaC
     cls.security = ClassSecurityInfo()
 
   @classmethod
-  def getSubclassList(metacls, cls):
+  def getSubclassList(meta_class, cls):
     """
     Returns classes deriving from cls
     """
-    return metacls.subclass_register.get(cls, [])
+    return meta_class.subclass_register.get(cls, [])
 
   def getAccessorHolderPropertyList(cls):
     """
@@ -145,10 +145,12 @@ class PortalTypeMetaClass(GhostBaseMetaC
     """
     cls.loadClass()
     property_dict = {}
+
     for klass in cls.mro():
-      if klass.__module__ == 'erp5.accessor_holder':
+      if klass.__module__.startswith('erp5.accessor_holder'):
         for property in klass._properties:
           property_dict.setdefault(property['id'], property)
+
     return property_dict.values()
 
   def resetAcquisition(cls):
@@ -210,36 +212,12 @@ class PortalTypeMetaClass(GhostBaseMetaC
     raise AttributeError
 
   def generatePortalTypeAccessors(cls, site, portal_type_category_list):
-    createAllCategoryAccessors(site,
-                               cls,
-                               portal_type_category_list,
-                               createExpressionContext(site, site))
-
-    # Properties defined on the portal type itself are generated in
-    # erp5.portal_type directly, but this is unusual case (only
-    # PDFTypeInformation seems to use it)
-    portal_type_property_list = getattr(cls, '_properties', None)
-    if portal_type_property_list:
-      setDefaultProperties(cls)
-
-    # make sure that category accessors from the portal type definition
-    # are generated, no matter what
-    # XXX this code is duplicated here, in PropertySheetTool, and in Base
-    # and anyway is ugly, as tuple-like registration does not help
-    for id, fake_accessor in cls._getPropertyHolderItemList():
-      if not isinstance(fake_accessor, tuple):
-        continue
-
-      if fake_accessor is PropertyHolder.WORKFLOW_METHOD_MARKER:
-        # Case 1 : a workflow method only
-        accessor = ERP5Base._doNothing
-      else:
-        # Case 2 : a workflow method over an accessor
-        (accessor_class, accessor_args, key) = fake_accessor
-        accessor = accessor_class(id, key, *accessor_args)
-
-      # Add the accessor to the accessor holder
-      setattr(cls, id, accessor)
+    category_tool = getattr(site, 'portal_categories', None)
+    for category_id in portal_type_category_list:
+      # we need to generate only categories defined on portal type
+      CategoryProperty.applyDefinitionOnAccessorHolder(cls,
+                                                       category_id,
+                                                       category_tool)
 
     portal_workflow = getattr(site, 'portal_workflow', None)
     if portal_workflow is None:
@@ -259,9 +237,8 @@ class PortalTypeMetaClass(GhostBaseMetaC
     for group in ERP5TypeInformation.defined_group_list:
       value = cls.__name__ in site._getPortalGroupedTypeSet(group)
       accessor_name = 'is' + UpperCase(group) + 'Type'
-      setattr(cls, accessor_name, ConstantGetter(accessor_name, group, value))
-      cls.declareProtected(Permissions.AccessContentsInformation,
-                           accessor_name)
+      method = ConstantGetter(accessor_name, group, value)
+      cls.registerAccessor(method, Permissions.AccessContentsInformation)
 
     from Products.ERP5Type.Cache import initializePortalCachingProperties
     initializePortalCachingProperties(site)
@@ -274,7 +251,7 @@ class PortalTypeMetaClass(GhostBaseMetaC
     cls.loadClass()
     result = PropertyHolder._getPropertyHolderItemList(cls)
     for parent in cls.mro():
-      if parent.__module__ == 'erp5.accessor_holder':
+      if parent.__module__.startswith('erp5.accessor_holder'):
         for x in parent.__dict__.items():
           if x[0] not in PropertyHolder.RESERVED_PROPERTY_SET:
             result.append(x)

Modified: erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/dynamic/portal_type_class.py [utf8] Wed Mar  2 13:19:02 2011
@@ -32,16 +32,14 @@ import os
 import inspect
 from types import ModuleType
 
-from dynamic_module import registerDynamicModule
-from accessor_holder import _generateBaseAccessorHolder, \
-    _generatePreferenceToolAccessorHolder
-
+from Products.ERP5Type.dynamic.dynamic_module import registerDynamicModule
 from Products.ERP5Type.mixin.temporary import TemporaryDocumentMixin
 from Products.ERP5Type.Base import Base, resetRegisteredWorkflowMethod
 from Products.ERP5Type.Globals import InitializeClass
 from Products.ERP5Type.Utils import setDefaultClassProperties
 from Products.ERP5Type import document_class_registry, mixin_class_registry
-from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
+from Products.ERP5Type.dynamic.accessor_holder import AccessorHolderModuleType, \
+    createAllAccessorHolderList
 
 from zLOG import LOG, ERROR, INFO, WARNING
 
@@ -59,71 +57,6 @@ def _importClass(classpath):
   except StandardError:
     raise ImportError('Could not import document class %s' % classpath)
 
-def _createAccessorHolderList(site,
-                              portal_type_name,
-                              property_sheet_name_set):
-  """
-  Create the accessor holder list with the given ZODB Property Sheets
-  """
-  from erp5 import accessor_holder
-
-  getPropertySheet = site.portal_property_sheets._getOb
-  accessor_holder_list = []
-
-  if "Base" in property_sheet_name_set:
-    # useless if Base Category is not yet here or if we're currently
-    # generating accessors for Base Categories
-    accessor_holder_class = _generateBaseAccessorHolder(site, accessor_holder)
-
-    if accessor_holder_class is not None:
-      accessor_holder_list.append(accessor_holder_class)
-
-  for property_sheet_name in property_sheet_name_set:
-    # LOG("ERP5Type.dynamic", INFO,
-    #     "Getting accessor holder for " + property_sheet_name)
-
-    try:
-      # Get the already generated accessor holder
-      accessor_holder_list.append(getattr(accessor_holder, property_sheet_name))
-
-    except AttributeError:
-      try:
-        property_sheet = getPropertySheet(property_sheet_name)
-      except KeyError:
-        LOG("ERP5Type.dynamic", WARNING,
-            "Ignoring missing Property Sheet " + property_sheet_name)
-
-        continue
-
-      # Generate the accessor holder as it has not been done yet
-      try:
-        accessor_holder_class = property_sheet.createAccessorHolder()
-      except Exception:
-        LOG("ERP5Type.dynamic", ERROR,
-            "Invalid Property Sheet " + property_sheet_name)
-        raise
-
-      accessor_holder_list.append(accessor_holder_class)
-
-      setattr(accessor_holder, property_sheet_name, accessor_holder_class)
-
-      # LOG("ERP5Type.dynamic", INFO,
-      #     "Created accessor holder for %s" % property_sheet_name)
-
-  # XXX a hook to add per-portal type accessor holders maybe?
-  if portal_type_name == "Preference Tool":
-    accessor_holder_class = \
-        _generatePreferenceToolAccessorHolder(site,
-                                              accessor_holder_list,
-                                              accessor_holder)
-
-    accessor_holder_list.insert(0, accessor_holder_class)
-
-  # LOG("ERP5Type.dynamic", INFO,
-  #     "Got accessor holder for %s: %s" % (property_sheet_name, accessor_holder_list))
-
-  return accessor_holder_list
-
 # Loading Cache Factory portal type would generate the accessor holder
 # for Cache Factory, itself defined with Standard Property thus
 # loading the portal type Standard Property, itself defined with
@@ -177,7 +110,6 @@ def generatePortalTypeClass(site, portal
 
   portal_type_category_list = []
   attribute_dict = dict(portal_type=portal_type_name,
-                        _properties=[],
                         _categories=[],
                         constraints=[])
 
@@ -269,80 +201,18 @@ def generatePortalTypeClass(site, portal
 
     property_sheet_generating_portal_type_set.add(portal_type_name)
 
-    property_sheet_tool = getattr(site, 'portal_property_sheets', None)
-
-    property_sheet_name_set = set()
-
-    # The Property Sheet Tool may be None if the code is updated but
-    # the BT has not been upgraded yet with portal_property_sheets
-    if property_sheet_tool is None:
-      if not getattr(site, '_v_bootstrapping', False):
-        LOG("ERP5Type.dynamic", WARNING,
-            "Property Sheet Tool was not found. Please update erp5_core "
-            "Business Template")
-      zodb_property_sheet_name_set = set()
-    else:
-      zodb_property_sheet_name_set = set(property_sheet_tool.objectIds())
-    if portal_type is not None:
-      # Get the Property Sheets defined on the portal_type and use the
-      # ZODB Property Sheet rather than the filesystem
-      for property_sheet in portal_type.getTypePropertySheetList():
-        if property_sheet in zodb_property_sheet_name_set:
-          property_sheet_name_set.add(property_sheet)
-
-      # PDFTypeInformation document class, for example, defines a
-      # method which generates dynamically properties and this is
-      # heavily used by egov
-      update_definition_dict = getattr(portal_type,
-                                       'updatePropertySheetDefinitionDict',
-                                       None)
-
-      if update_definition_dict is not None and not \
-         update_definition_dict.__module__.startswith('Products.ERP5Type.ERP5Type'):
-        try:
-          update_definition_dict(attribute_dict)
-        except AttributeError:
-          pass
-
-    # Only kept for backward-compatibility as Preference and System
-    # Preference have Preference Type as portal type, which define
-    # getTypePropertySheetList properly and, likewise, Preference Tool
-    # has Preference Tool Type as its portal type
-    if portal_type_name in ("Preference Tool",
-                            "Preference",
-                            "System Preference"):
-       if portal_type is None or \
-          not portal_type.getPortalType().startswith(portal_type_name):
-         for property_sheet in zodb_property_sheet_name_set:
-           if property_sheet.endswith('Preference'):
-             property_sheet_name_set.add(property_sheet)
-
-    # Get the Property Sheets defined on the document and its bases
-    # recursively
-    from Products.ERP5Type.Base import getClassPropertyList
-    for property_sheet in getClassPropertyList(klass):
-      # If the Property Sheet is a string, then this is a ZODB
-      # Property Sheet
-      #
-      # NOTE: The Property Sheets of a document should be given as a
-      #       string from now on
-      if not isinstance(property_sheet, basestring):
-        property_sheet = property_sheet.__name__
-      if property_sheet in zodb_property_sheet_name_set:
-        property_sheet_name_set.add(property_sheet)
-
-    if property_sheet_name_set:
-      # Initialize ZODB Property Sheets accessor holders
-      accessor_holder_list = _createAccessorHolderList(site,
+    # Initialize ZODB Property Sheets accessor holders
+    accessor_holder_list = createAllAccessorHolderList(site,
                                                        portal_type_name,
-                                                       property_sheet_name_set)
+                                                       portal_type,
+                                                       klass)
 
-      base_category_set = set(attribute_dict['_categories'])
-      for accessor_holder in accessor_holder_list:
-        base_category_set.update(accessor_holder._categories)
-        attribute_dict['constraints'].extend(accessor_holder.constraints)
+    base_category_set = set(attribute_dict['_categories'])
+    for accessor_holder in accessor_holder_list:
+      base_category_set.update(accessor_holder._categories)
+      attribute_dict['constraints'].extend(accessor_holder.constraints)
 
-      attribute_dict['_categories'] = list(base_category_set)
+    attribute_dict['_categories'] = list(base_category_set)
 
     property_sheet_generating_portal_type_set.remove(portal_type_name)
 
@@ -388,15 +258,29 @@ def initializeDynamicModules():
       for example classes created through ClassTool that are in
       $INSTANCE_HOME/Document
     erp5.accessor_holder
-      holds accessors of ZODB Property Sheets
+      holds accessor holders common to ZODB Property Sheets and Portal Types
+    erp5.accessor_holder.property_sheet
+      holds accessor holders of ZODB Property Sheets
+    erp5.accessor_holder.portal_type
+      holds accessors holders of Portal Types
   """
   erp5 = ModuleType("erp5")
   sys.modules["erp5"] = erp5
   erp5.document = ModuleType("erp5.document")
   sys.modules["erp5.document"] = erp5.document
-  erp5.accessor_holder = ModuleType("erp5.accessor_holder")
+  erp5.accessor_holder = AccessorHolderModuleType("erp5.accessor_holder")
   sys.modules["erp5.accessor_holder"] = erp5.accessor_holder
 
+  erp5.accessor_holder.property_sheet = \
+      AccessorHolderModuleType("erp5.accessor_holder.property_sheet")
+
+  sys.modules["erp5.accessor_holder.property_sheet"] = \
+      erp5.accessor_holder.property_sheet
+
+  erp5.accessor_holder.portal_type = registerDynamicModule(
+    'erp5.accessor_holder.portal_type',
+    AccessorHolderModuleType)
+
   portal_type_container = registerDynamicModule('erp5.portal_type',
                                                 generateLazyPortalTypeClass)
 
@@ -502,10 +386,14 @@ def synchronizeDynamicModules(context, f
                                                 inspect.isclass):
       klass.restoreGhostState()
 
-    # Clear accessor holders of ZODB Property Sheets
-    for property_sheet_id in erp5.accessor_holder.__dict__.keys():
-      if not property_sheet_id.startswith('__'):
-        delattr(erp5.accessor_holder, property_sheet_id)
+    # Clear accessor holders of ZODB Property Sheets and Portal Types
+    erp5.accessor_holder.clear()
+    erp5.accessor_holder.property_sheet.clear()
+
+    for name in erp5.accessor_holder.portal_type.__dict__.keys():
+      if name[0] != '_':
+        delattr(erp5.accessor_holder.portal_type, name)
+
   finally:
     Base.aq_method_lock.release()
 

Modified: erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py?rev=43892&r1=43891&r2=43892&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/tests/testDynamicClassGeneration.py [utf8] Wed Mar  2 13:19:02 2011
@@ -595,14 +595,16 @@ class TestZodbPropertySheet(ERP5TypeTest
 
       # The accessor holder will be generated once the new Person will
       # be created as Person type has test Property Sheet
-      self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+      self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+                              'TestMigration')
 
       new_person = portal.person_module.newContent(
         id='testAssignZodbPropertySheet', portal_type='Person')
 
-      self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
+      self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+                              'TestMigration')
 
-      self.assertTrue(erp5.accessor_holder.TestMigration in \
+      self.assertTrue(erp5.accessor_holder.property_sheet.TestMigration in \
                       erp5.portal_type.Person.mro())
 
       # Check that the accessors have been properly created for all
@@ -677,7 +679,7 @@ class TestZodbPropertySheet(ERP5TypeTest
       new_person = portal.person_module.newContent(
         id='testAssignZodbPropertySheet', portal_type='Person')
 
-      self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+      self.failIfHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
       self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign')
 
     finally:
@@ -687,15 +689,18 @@ class TestZodbPropertySheet(ERP5TypeTest
   def _checkAddPropertyToZodbPropertySheet(self,
                                           new_property_function,
                                           added_accessor_name):
-    import erp5.accessor_holder
+    import erp5.accessor_holder.property_sheet
 
-    self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+    self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+                            'TestMigration')
 
     new_property_function('add')
     self._forceTestAccessorHolderGeneration()
 
-    self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
-    self.assertHasAttribute(erp5.accessor_holder.TestMigration,
+    self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+                            'TestMigration')
+
+    self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
                             added_accessor_name)
 
   def testAddStandardPropertyToZodbPropertySheet(self):
@@ -738,15 +743,18 @@ class TestZodbPropertySheet(ERP5TypeTest
                                              change_setter_func,
                                              new_value,
                                              changed_accessor_name):
-    import erp5.accessor_holder
+    import erp5.accessor_holder.property_sheet
 
-    self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
+    self.failIfHasAttribute(erp5.accessor_holder.property_sheet,
+                            'TestMigration')
 
     change_setter_func(new_value)
     self._forceTestAccessorHolderGeneration()
 
-    self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
-    self.assertHasAttribute(erp5.accessor_holder.TestMigration,
+    self.assertHasAttribute(erp5.accessor_holder.property_sheet,
+                            'TestMigration')
+
+    self.assertHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
                             changed_accessor_name)
 
   def testChangeStandardPropertyOfZodbPropertySheet(self):
@@ -798,7 +806,7 @@ class TestZodbPropertySheet(ERP5TypeTest
     Delete the given property from the test Property Sheet and check
     whether its corresponding accessor is not there anymore
     """
-    import erp5.accessor_holder
+    import erp5.accessor_holder.property_sheet
 
     self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
 
@@ -807,8 +815,8 @@ class TestZodbPropertySheet(ERP5TypeTest
     self.test_property_sheet.deleteContent(property_id)
     self._forceTestAccessorHolderGeneration()
 
-    self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
-    self.failIfHasAttribute(erp5.accessor_holder.TestMigration,
+    self.assertHasAttribute(erp5.accessor_holder.property_sheet, 'TestMigration')
+    self.failIfHasAttribute(erp5.accessor_holder.property_sheet.TestMigration,
                             accessor_name)
 
   def testDeleteStandardPropertyFromZodbPropertySheet(self):



More information about the Erp5-report mailing list