[Erp5-report] r35103 jerome - /erp5/trunk/products/ERP5Type/Base.py

nobody at svn.erp5.org nobody at svn.erp5.org
Fri May 7 13:49:08 CEST 2010


Author: jerome
Date: Fri May  7 13:49:08 2010
New Revision: 35103

URL: http://svn.erp5.org?rev=35103&view=rev
Log:
Use another strategy to generate accessors.
Accessors are generated during the first call to _aq_dynamic, and generating
accessors requires to access to some tools such as portal_types, that's why
this method use to contain a list of names that should be ignored to prevent
infinite loop (portal_types, portal_workflow ...)
Now we store in a thread local variable the information that we are in the
process of generating accessors, and return immediatly next time we enter
_aq_dynamic, to be able to access required tools.
As generating accessors for one type sometimes triggers accessors generation
for a dependant type (Base Category, Types Tool), then we store in this
variable the currently generated aq_key, not only the fact that we are
generating.

Modified:
    erp5/trunk/products/ERP5Type/Base.py

Modified: erp5/trunk/products/ERP5Type/Base.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5Type/Base.py?rev=35103&r1=35102&r2=35103&view=diff
==============================================================================
--- erp5/trunk/products/ERP5Type/Base.py [utf8] (original)
+++ erp5/trunk/products/ERP5Type/Base.py [utf8] Fri May  7 13:49:08 2010
@@ -31,6 +31,7 @@
 from copy import copy
 import warnings
 import types
+from threading import local
 
 from Products.ERP5Type.Globals import InitializeClass, DTMLFile
 from AccessControl import ClassSecurityInfo
@@ -564,7 +565,7 @@
     # Always do it before processing klass.property_sheets (for compatibility).
     # Because of the order we generate accessors, it is still possible
     # to overload data access for some accessors.
-    ptype_object = portal.portal_types._getOb(ptype, None)
+    ptype_object = portal.portal_types.getTypeInfo(ptype)
     if ptype_object is not None:
       ptype_object.updatePropertySheetDefinitionDict(ps_definition_dict)
 
@@ -755,6 +756,7 @@
 
   # Dynamic method acquisition system (code generation)
   aq_method_generated = {}
+  aq_method_generating = local()
   aq_portal_type = {}
   aq_related_generated = 0
 
@@ -886,99 +888,107 @@
       return accessor
     except KeyError:
       property_holder = None
-
-    if id in ('portal_types', 'portal_url', 'portal_workflow'):
-      # This is required to precent infinite loop (we need to access portal_types tool)
-      return None
-
-    # Proceed with property generation
-    if self.isTempObject():
-      # If self is a temporary object, generate methods for the base
-      # document class rather than for the temporary document class.
-      # Otherwise, instances of the base document class would fail
-      # in calling such methods, because they are not instances of
-      # the temporary document class.
-      klass = klass.__bases__[0]
-    generated = 0 # Prevent infinite loops
-
-    # Generate class methods
-    if not Base.aq_method_generated.has_key(klass):
-      initializeClassDynamicProperties(self, klass)
-      generated = 1
-
-    # Iterate until an ERP5 Site is obtained.
-    portal = self.getPortalObject()
-    while portal.portal_type != 'ERP5 Site':
-      portal = portal.aq_parent.aq_inner.getPortalObject()
-
-    # Generate portal_type methods
-    if not Base.aq_portal_type.has_key(aq_key):
-      if ptype == 'Preference':
-        # XXX-JPS this should be moved to Preference class
-        from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList
-        updatePreferenceClassPropertySheetList()
-      initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal)
-      generated = 1
-
-    # Generate Related Accessors
-    if not Base.aq_related_generated:
-      from Utils import createRelatedValueAccessors
-      generated = 1
-      portal_types = getToolByName(portal, 'portal_types', None)
-      generated_bid = {}
-      econtext = createExpressionContext(object=self, portal=portal)
-      for pid, ps in PropertySheet.__dict__.iteritems():
-        if pid[0] != '_':
-          base_category_list = []
-          for cat in getattr(ps, '_categories', ()):
-            if isinstance(cat, Expression):
-              result = cat(econtext)
-              if isinstance(result, (list, tuple)):
-                base_category_list.extend(result)
+      if getattr(Base.aq_method_generating, 'aq_key', None) == aq_key:
+        # We are already generating for this aq_key, return not to generate
+        # again.
+        return None
+
+    # Store that we are generating for this aq_key, then when we will recurse
+    # in _aq_dynamic during generation for this aq_key, we'll return to prevent
+    # infinite loops. While generating for one aq_key, we will probably have to
+    # generate for another aq_key, a typical example is that to generate
+    # methods for a document, we'll have to generate methods for Types Tool and
+    # Base Category portal.
+    Base.aq_method_generating.aq_key = aq_key
+    try:
+      # Proceed with property generation
+      if self.isTempObject():
+        # If self is a temporary object, generate methods for the base
+        # document class rather than for the temporary document class.
+        # Otherwise, instances of the base document class would fail
+        # in calling such methods, because they are not instances of
+        # the temporary document class.
+        klass = klass.__bases__[0]
+
+      generated = False # Prevent infinite loops
+
+      # Generate class methods
+      if klass not in Base.aq_method_generated:
+        initializeClassDynamicProperties(self, klass)
+        generated = True
+
+      # Iterate until an ERP5 Site is obtained.
+      portal = self.getPortalObject()
+      while portal.portal_type != 'ERP5 Site':
+        portal = portal.aq_parent.aq_inner.getPortalObject()
+
+      # Generate portal_type methods
+      if aq_key not in Base.aq_portal_type:
+        if ptype == 'Preference':
+          # XXX-JPS this should be moved to Preference class
+          from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList
+          updatePreferenceClassPropertySheetList()
+        initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal)
+        generated = True
+
+      # Generate Related Accessors
+      if not Base.aq_related_generated:
+        from Utils import createRelatedValueAccessors
+        generated = True
+        portal_types = getToolByName(portal, 'portal_types', None)
+        generated_bid = {}
+        econtext = createExpressionContext(object=self, portal=portal)
+        for pid, ps in PropertySheet.__dict__.iteritems():
+          if pid[0] != '_':
+            base_category_list = []
+            for cat in getattr(ps, '_categories', ()):
+              if isinstance(cat, Expression):
+                result = cat(econtext)
+                if isinstance(result, (list, tuple)):
+                  base_category_list.extend(result)
+                else:
+                  base_category_list.append(result)
               else:
-                base_category_list.append(result)
-            else:
-              base_category_list.append(cat)
-          for bid in base_category_list:
-            if bid not in generated_bid:
-              #LOG( "Create createRelatedValueAccessors %s" % bid,0,'')
+                base_category_list.append(cat)
+            for bid in base_category_list:
+              if bid not in generated_bid:
+                createRelatedValueAccessors(property_holder, bid)
+                generated_bid[bid] = 1
+        for ptype in portal_types.listTypeInfo():
+          for bid in ptype.getTypeBaseCategoryList():
+            if bid not in generated_bid :
               createRelatedValueAccessors(property_holder, bid)
               generated_bid[bid] = 1
-      for ptype in portal_types.objectValues():
-        for bid in ptype.getTypeBaseCategoryList():
-          if bid not in generated_bid :
-            createRelatedValueAccessors(property_holder, bid)
-            generated_bid[bid] = 1
-
-      Base.aq_related_generated = 1
-
-    # Generate preference methods (since side effect is to reset Preference accessors)
-    # XXX-JPS - This should be moved to PreferenceTool
-    if not Base.aq_preference_generated:
-      try :
-        from Products.ERP5Form.PreferenceTool import createPreferenceToolAccessorList
-        from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList
-        updatePreferenceClassPropertySheetList()
-        createPreferenceToolAccessorList(portal)
-      except ImportError, e :
-        LOG('Base._aq_dynamic', WARNING,
-            'unable to create methods for PreferenceTool', e)
-        raise
-      Base.aq_preference_generated = 1
-
-    # Always try to return something after generation
-    if generated:
-      # We suppose that if we reach this point
-      # then it means that all code generation has succeeded
-      # (no except should hide that). We can safely return None
-      # if id does not exist as a dynamic property
-      # Baseline: accessor generation failures should always
-      #           raise an exception up to the user
-      #LOG('_aq_dynamic', 0, 'getattr self = %r, id = %r' % (self, id))
-      return getattr(self, id, None)
+
+        Base.aq_related_generated = 1
+
+      # Generate preference methods (since side effect is to reset Preference accessors)
+      # XXX-JPS - This should be moved to PreferenceTool
+      if not Base.aq_preference_generated:
+        try :
+          from Products.ERP5Form.PreferenceTool import createPreferenceToolAccessorList
+          from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList
+          updatePreferenceClassPropertySheetList()
+          createPreferenceToolAccessorList(portal)
+        except ImportError, e :
+          LOG('Base._aq_dynamic', WARNING,
+              'unable to create methods for PreferenceTool', e)
+          raise
+        Base.aq_preference_generated = 1
+
+      # Always try to return something after generation
+      if generated:
+        # We suppose that if we reach this point
+        # then it means that all code generation has succeeded
+        # (no except should hide that). We can safely return None
+        # if id does not exist as a dynamic property
+        # Baseline: accessor generation failures should always
+        #           raise an exception up to the user
+        return getattr(self, id, None)
+    finally:
+      Base.aq_method_generating.aq_key = None
 
     # Proceed with standard acquisition
-    #LOG('_aq_dynamic', 0, 'not generated; return None for id = %r, self = %r' % (id, self))
     return None
 
   psyco.bind(_aq_dynamic)




More information about the Erp5-report mailing list