Commit 637c61d8 authored by Jérome Perrin's avatar Jérome Perrin

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.


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