diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py index e6da2e808c2c6bbddbbfefee1820868b78b378f7..c2ab0f7b055195532b117eb8d7f0eef6f2efedde 100644 --- a/product/ERP5Type/Base.py +++ b/product/ERP5Type/Base.py @@ -31,6 +31,7 @@ from struct import unpack 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 @@ def initializePortalTypeDynamicProperties(self, klass, ptype, aq_key, portal): # 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 @@ class Base( CopyContainer, # Dynamic method acquisition system (code generation) aq_method_generated = {} + aq_method_generating = local() aq_portal_type = {} aq_related_generated = 0 @@ -886,99 +888,107 @@ class Base( CopyContainer, return accessor except KeyError: 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'): - # 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) + # 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)