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,11 +888,19 @@ class Base( CopyContainer, ...@@ -886,11 +888,19 @@ 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:
if id in ('portal_types', 'portal_url', 'portal_workflow'): # We are already generating for this aq_key, return not to generate
# This is required to precent infinite loop (we need to access portal_types tool) # again.
return None 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 # Proceed with property generation
if self.isTempObject(): if self.isTempObject():
# If self is a temporary object, generate methods for the base # If self is a temporary object, generate methods for the base
...@@ -899,12 +909,13 @@ class Base( CopyContainer, ...@@ -899,12 +909,13 @@ class Base( CopyContainer,
# in calling such methods, because they are not instances of # in calling such methods, because they are not instances of
# the temporary document class. # the temporary document class.
klass = klass.__bases__[0] klass = klass.__bases__[0]
generated = 0 # Prevent infinite loops
generated = False # Prevent infinite loops
# Generate class methods # Generate class methods
if not Base.aq_method_generated.has_key(klass): if klass not in Base.aq_method_generated:
initializeClassDynamicProperties(self, klass) initializeClassDynamicProperties(self, klass)
generated = 1 generated = True
# Iterate until an ERP5 Site is obtained. # Iterate until an ERP5 Site is obtained.
portal = self.getPortalObject() portal = self.getPortalObject()
...@@ -912,18 +923,18 @@ class Base( CopyContainer, ...@@ -912,18 +923,18 @@ class Base( CopyContainer,
portal = portal.aq_parent.aq_inner.getPortalObject() portal = portal.aq_parent.aq_inner.getPortalObject()
# Generate portal_type methods # Generate portal_type methods
if not Base.aq_portal_type.has_key(aq_key): if aq_key not in Base.aq_portal_type:
if ptype == 'Preference': if ptype == 'Preference':
# XXX-JPS this should be moved to Preference class # XXX-JPS this should be moved to Preference class
from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList from Products.ERP5Form.PreferenceTool import updatePreferenceClassPropertySheetList
updatePreferenceClassPropertySheetList() updatePreferenceClassPropertySheetList()
initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal) initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal)
generated = 1 generated = True
# Generate Related Accessors # Generate Related Accessors
if not Base.aq_related_generated: if not Base.aq_related_generated:
from Utils import createRelatedValueAccessors from Utils import createRelatedValueAccessors
generated = 1 generated = True
portal_types = getToolByName(portal, 'portal_types', None) portal_types = getToolByName(portal, 'portal_types', None)
generated_bid = {} generated_bid = {}
econtext = createExpressionContext(object=self, portal=portal) econtext = createExpressionContext(object=self, portal=portal)
...@@ -941,10 +952,9 @@ class Base( CopyContainer, ...@@ -941,10 +952,9 @@ class Base( CopyContainer,
base_category_list.append(cat) base_category_list.append(cat)
for bid in base_category_list: for bid in base_category_list:
if bid not in generated_bid: if bid not in generated_bid:
#LOG( "Create createRelatedValueAccessors %s" % bid,0,'')
createRelatedValueAccessors(property_holder, bid) createRelatedValueAccessors(property_holder, bid)
generated_bid[bid] = 1 generated_bid[bid] = 1
for ptype in portal_types.objectValues(): for ptype in portal_types.listTypeInfo():
for bid in ptype.getTypeBaseCategoryList(): for bid in ptype.getTypeBaseCategoryList():
if bid not in generated_bid : if bid not in generated_bid :
createRelatedValueAccessors(property_holder, bid) createRelatedValueAccessors(property_holder, bid)
...@@ -974,11 +984,11 @@ class Base( CopyContainer, ...@@ -974,11 +984,11 @@ class Base( CopyContainer,
# if id does not exist as a dynamic property # if id does not exist as a dynamic property
# Baseline: accessor generation failures should always # Baseline: accessor generation failures should always
# raise an exception up to the user # raise an exception up to the user
#LOG('_aq_dynamic', 0, 'getattr self = %r, id = %r' % (self, id))
return getattr(self, id, None) return getattr(self, id, None)
finally:
Base.aq_method_generating.aq_key = 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