Commit e2f3129d authored by Arnaud Fontaine's avatar Arnaud Fontaine

Fix infinite recursion on ZODB Property Sheets accessors generation

and define only one accessor holder module (ZODB Property Sheets)



git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@40345 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent cf5093a9
...@@ -266,7 +266,7 @@ class PropertySheetTool(BaseTool): ...@@ -266,7 +266,7 @@ class PropertySheetTool(BaseTool):
return self._createCommonPropertySheetAccessorHolder( return self._createCommonPropertySheetAccessorHolder(
property_holder, property_holder,
property_sheet.getId(), property_sheet.getId(),
'erp5.zodb_accessor_holder') 'erp5.accessor_holder')
security.declareProtected(Permissions.ManagePortal, security.declareProtected(Permissions.ManagePortal,
'getPropertyAvailablePermissionList') 'getPropertyAvailablePermissionList')
......
...@@ -37,7 +37,6 @@ from Products.ERP5Type.Base import _aq_reset ...@@ -37,7 +37,6 @@ from Products.ERP5Type.Base import _aq_reset
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from Products.ERP5Type.Utils import setDefaultClassProperties from Products.ERP5Type.Utils import setDefaultClassProperties
from Products.ERP5Type import document_class_registry, mixin_class_registry from Products.ERP5Type import document_class_registry, mixin_class_registry
from Products.ERP5Type import PropertySheet as FilesystemPropertySheet
from zope.interface import classImplements from zope.interface import classImplements
from zLOG import LOG, ERROR, INFO from zLOG import LOG, ERROR, INFO
...@@ -66,6 +65,9 @@ def _fillAccessorHolderList(accessor_holder_list, ...@@ -66,6 +65,9 @@ def _fillAccessorHolderList(accessor_holder_list,
could be coming either from the filesystem or ZODB) could be coming either from the filesystem or ZODB)
""" """
for property_sheet_name in property_sheet_name_set: for property_sheet_name in property_sheet_name_set:
# LOG("ERP5Type.dynamic", INFO,
# "Getting accessor holder for " + property_sheet_name)
try: try:
# Get the already generated accessor holder # Get the already generated accessor holder
accessor_holder_list.append(getattr(accessor_holder_module, accessor_holder_list.append(getattr(accessor_holder_module,
...@@ -79,19 +81,31 @@ def _fillAccessorHolderList(accessor_holder_list, ...@@ -79,19 +81,31 @@ def _fillAccessorHolderList(accessor_holder_list,
property_sheet_name)) property_sheet_name))
except AttributeError: except AttributeError:
# Not too critical
LOG("ERP5Type.dynamic", ERROR, LOG("ERP5Type.dynamic", ERROR,
"Ignoring missing Property Sheet " + property_sheet_name) "Ignoring missing Property Sheet " + property_sheet_name)
else: raise
accessor_holder_list.append(accessor_holder_class)
setattr(accessor_holder_module, property_sheet_name, setattr(accessor_holder_module, property_sheet_name,
accessor_holder_class) accessor_holder_class)
accessor_holder_list.append(accessor_holder_class) # LOG("ERP5Type.dynamic", INFO,
# "Created accessor holder for %s in %s" % (property_sheet_name,
# accessor_holder_module))
LOG("ERP5Type.dynamic", INFO, # LOG("ERP5Type.dynamic", INFO,
"Created accessor holder for %s in %s" % (property_sheet_name, # "Got accessor holder for " + property_sheet_name)
accessor_holder_module))
# 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
# Standard Property and so on...
#
# NOTE: only the outer Property Sheets is stored in the accessor
# holder module
property_sheet_generating_portal_type_set = set()
# 'Types Tool' is required to access 'site.portal_types' and the # 'Types Tool' is required to access 'site.portal_types' and the
# former requires 'Base Type'. Thus, 'generating' is meaningful to # former requires 'Base Type'. Thus, 'generating' is meaningful to
...@@ -120,6 +134,8 @@ def generatePortalTypeClass(portal_type_name): ...@@ -120,6 +134,8 @@ def generatePortalTypeClass(portal_type_name):
from Products.ERP5.ERP5Site import getSite from Products.ERP5.ERP5Site import getSite
site = getSite() site = getSite()
# LOG("ERP5Type.dynamic", INFO, "Loading portal type " + portal_type_name)
global core_portal_type_class_dict global core_portal_type_class_dict
if portal_type_name in core_portal_type_class_dict: if portal_type_name in core_portal_type_class_dict:
...@@ -132,6 +148,9 @@ def generatePortalTypeClass(portal_type_name): ...@@ -132,6 +148,9 @@ def generatePortalTypeClass(portal_type_name):
klass = _importClass(document_class_registry.get( klass = _importClass(document_class_registry.get(
core_portal_type_class_dict[portal_type_name]['type_class'])) core_portal_type_class_dict[portal_type_name]['type_class']))
# LOG("ERP5Type.dynamic", INFO,
# "Loaded portal type %s (INNER)" % portal_type_name)
# Don't do anything else, just allow to load fully the outer # Don't do anything else, just allow to load fully the outer
# portal type class # portal type class
return ((klass,), [], {}) return ((klass,), [], {})
...@@ -184,44 +203,57 @@ def generatePortalTypeClass(portal_type_name): ...@@ -184,44 +203,57 @@ def generatePortalTypeClass(portal_type_name):
klass = _importClass(type_class_path) klass = _importClass(type_class_path)
global property_sheet_generating_portal_type_set
accessor_holder_list = [] accessor_holder_list = []
## Disabled because there will be no commit of if portal_type_name not in property_sheet_generating_portal_type_set:
## type_zodb_property_sheet, only use for testing ATM # LOG("ERP5Type.dynamic", INFO,
# "Filling accessor holder list for portal_type " + portal_type_name)
# import erp5
property_sheet_generating_portal_type_set.add(portal_type_name)
# # Initialize filesystem Property Sheets accessor holders
# _fillAccessorHolderList( property_sheet_set = set()
# accessor_holder_list,
# site.portal_property_sheets.createFilesystemPropertySheetAccessorHolder, #### Waiting for Yoshinori's advice before enabling this code
# set(portal_type.getTypePropertySheetList() or ()), # if portal_type is not None:
# erp5.filesystem_accessor_holder, # # Get the Property Sheets defined on the portal_type
# FilesystemPropertySheet) # property_sheet_set.update(portal_type.getTypeZodbPropertySheetList() \
# or ())
# # Initialize ZODB Property Sheets accessor holders
# _fillAccessorHolderList( #### The Property Sheets specified in the property_sheets
# accessor_holder_list, #### attribute of a Document are only filesystem-based, thus for
# site.portal_property_sheets.createZodbPropertySheetAccessorHolder, #### now it has to be done in the Portal Types themselves
# set(portal_type.getTypeZodbPropertySheetList() or ()), # # Get the Property Sheets defined on the document and its bases
# erp5.zodb_accessor_holder, # # recursively
# site.portal_property_sheets)
# # XXX: for now, we have PropertySheet classes defined in
# # property_sheets attribute of Document, but there will be only
# # string at the end
# from Products.ERP5Type.Base import getClassPropertyList # from Products.ERP5Type.Base import getClassPropertyList
# _fillAccessorHolderList( # for property_sheet in getClassPropertyList(klass):
# accessor_holder_list, # if isinstance(property_sheet, basestring):
# site.portal_property_sheets.createFilesystemPropertySheetAccessorHolder, # property_sheet_name = property_sheet
# [ property_sheet.__name__ for property_sheet in \ # # FIXME: The Property Sheets of a document should be given as a
# getClassPropertyList(klass) ], # # string from now on, but to avoid changing all the code now, we
# erp5.filesystem_accessor_holder, # # just get the class name of the filesystem Property Sheet
# FilesystemPropertySheet) # else:
# property_sheet_name = property_sheet.__name__
# property_sheet_set.add(property_sheet_name)
import erp5
if property_sheet_set:
# Initialize ZODB Property Sheets accessor holders
_fillAccessorHolderList(
accessor_holder_list,
site.portal_property_sheets.createZodbPropertySheetAccessorHolder,
property_sheet_set,
erp5.accessor_holder,
site.portal_property_sheets)
property_sheet_generating_portal_type_set.remove(portal_type_name)
# LOG("ERP5Type.dynamic", INFO, # LOG("ERP5Type.dynamic", INFO,
# "%s: accessor_holder_list: %s" % (portal_type_name, # "Filled accessor holder list for portal_type %s (%s)" % \
# accessor_holder_list)) # (portal_type_name, accessor_holder_list))
mixin_path_list = [] mixin_path_list = []
if mixin_list: if mixin_list:
...@@ -259,23 +291,15 @@ def initializeDynamicModules(): ...@@ -259,23 +291,15 @@ def initializeDynamicModules():
holds document classes that have no physical import path, holds document classes that have no physical import path,
for example classes created through ClassTool that are in for example classes created through ClassTool that are in
$INSTANCE_HOME/Document $INSTANCE_HOME/Document
erp5.zodb_accessor_holder erp5.accessor_holder
holds accessors of ZODB Property Sheets holds accessors of ZODB Property Sheets
erp5.filesystem_accessor_holder
holds accessors of filesystem Property Sheets
XXX: there should be only one accessor_holder once the code is
stable and all the Property Sheets have been migrated
""" """
erp5 = ModuleType("erp5") erp5 = ModuleType("erp5")
sys.modules["erp5"] = erp5 sys.modules["erp5"] = erp5
erp5.document = ModuleType("erp5.document") erp5.document = ModuleType("erp5.document")
sys.modules["erp5.document"] = erp5.document sys.modules["erp5.document"] = erp5.document
erp5.accessor_holder = ModuleType("erp5.accessor_holder")
erp5.zodb_accessor_holder = ModuleType("erp5.zodb_accessor_holder") sys.modules["erp5.accessor_holder"] = erp5.accessor_holder
sys.modules["erp5.zodb_accessor_holder"] = erp5.zodb_accessor_holder
erp5.filesystem_accessor_holder = ModuleType("erp5.filesystem_accessor_holder")
sys.modules["erp5.filesystem_accessor_holder"] = erp5.filesystem_accessor_holder
portal_type_container = registerDynamicModule('erp5.portal_type', portal_type_container = registerDynamicModule('erp5.portal_type',
generateLazyPortalTypeClass) generateLazyPortalTypeClass)
...@@ -317,18 +341,6 @@ def initializeDynamicModules(): ...@@ -317,18 +341,6 @@ def initializeDynamicModules():
erp5.temp_portal_type = registerDynamicModule('erp5.temp_portal_type', erp5.temp_portal_type = registerDynamicModule('erp5.temp_portal_type',
loadTempPortalTypeClass) loadTempPortalTypeClass)
def _clearAccessorHolderModule(module):
"""
Clear the given accessor holder module (either for filesystem or
ZODB)
XXX: Merge into synchronizeDynamicModules as soon as we get rid of
these two accessor holder modules
"""
for property_sheet_id in module.__dict__.keys():
if not property_sheet_id.startswith('__'):
delattr(module, property_sheet_id)
last_sync = 0 last_sync = 0
def synchronizeDynamicModules(context, force=False): def synchronizeDynamicModules(context, force=False):
""" """
...@@ -364,10 +376,9 @@ def synchronizeDynamicModules(context, force=False): ...@@ -364,10 +376,9 @@ def synchronizeDynamicModules(context, force=False):
klass.restoreGhostState() klass.restoreGhostState()
# Clear accessor holders of ZODB Property Sheets # Clear accessor holders of ZODB Property Sheets
_clearAccessorHolderModule(erp5.zodb_accessor_holder) for property_sheet_id in erp5.accessor_holder.__dict__.keys():
if not property_sheet_id.startswith('__'):
# Clear accessor holders of filesystem Property Sheets delattr(erp5.accessor_holder, property_sheet_id)
_clearAccessorHolderModule(erp5.filesystem_accessor_holder)
# Necessary because accessors are wrapped in WorkflowMethod by # Necessary because accessors are wrapped in WorkflowMethod by
# _aq_dynamic (performed in createAccessorHolder) # _aq_dynamic (performed in createAccessorHolder)
......
...@@ -96,4 +96,4 @@ class ConstraintMixin(Predicate): ...@@ -96,4 +96,4 @@ class ConstraintMixin(Predicate):
XXX: remove as soon as the code is stable XXX: remove as soon as the code is stable
""" """
return self.asContext().aq_base return self.asContext()
...@@ -462,14 +462,14 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -462,14 +462,14 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# The accessor holder will be generated once the new Person will # The accessor holder will be generated once the new Person will
# be created as Person type has test Property Sheet # be created as Person type has test Property Sheet
self.failIfHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
new_person = portal.person_module.newContent( new_person = portal.person_module.newContent(
id='testAssignZodbPropertySheet', portal_type='Person') id='testAssignZodbPropertySheet', portal_type='Person')
self.assertHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
self.assertTrue(erp5.zodb_accessor_holder.TestMigration in \ self.assertTrue(erp5.accessor_holder.TestMigration in \
erp5.portal_type.Person.mro()) erp5.portal_type.Person.mro())
# Check that the accessors have been properly created for all # Check that the accessors have been properly created for all
...@@ -523,7 +523,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -523,7 +523,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
# workflow and the ID of the interaction and where the value is # workflow and the ID of the interaction and where the value is
# a boolean stating whether the transition method has already # a boolean stating whether the transition method has already
# been called before. Thus, the next statement may not reset # been called before. Thus, the next statement may not reset
# erp5.zodb_accessor_holder as loading Person portal type calls # erp5.accessor_holder as loading Person portal type calls
# '_setType*' # '_setType*'
transaction.commit() transaction.commit()
...@@ -546,7 +546,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -546,7 +546,7 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
id='testAssignZodbPropertySheet', portal_type='Person') id='testAssignZodbPropertySheet', portal_type='Person')
self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign') self.failIfHasAttribute(new_person, 'getTestStandardPropertyAssign')
self.failIfHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
finally: finally:
if new_person is not None: if new_person is not None:
...@@ -555,15 +555,15 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -555,15 +555,15 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
def _checkAddPropertyToZodbPropertySheet(self, def _checkAddPropertyToZodbPropertySheet(self,
new_property_function, new_property_function,
added_accessor_name): added_accessor_name):
import erp5.zodb_accessor_holder import erp5.accessor_holder
self.failIfHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
new_property_function('add') new_property_function('add')
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
self.assertHasAttribute(erp5.zodb_accessor_holder.TestMigration, self.assertHasAttribute(erp5.accessor_holder.TestMigration,
added_accessor_name) added_accessor_name)
def testAddStandardPropertyToZodbPropertySheet(self): def testAddStandardPropertyToZodbPropertySheet(self):
...@@ -606,15 +606,15 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -606,15 +606,15 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
change_setter_func, change_setter_func,
new_value, new_value,
changed_accessor_name): changed_accessor_name):
import erp5.zodb_accessor_holder import erp5.accessor_holder
self.failIfHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
change_setter_func(new_value) change_setter_func(new_value)
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
self.assertHasAttribute(erp5.zodb_accessor_holder.TestMigration, self.assertHasAttribute(erp5.accessor_holder.TestMigration,
changed_accessor_name) changed_accessor_name)
def testChangeStandardPropertyOfZodbPropertySheet(self): def testChangeStandardPropertyOfZodbPropertySheet(self):
...@@ -666,17 +666,17 @@ class TestZodbPropertySheet(ERP5TypeTestCase): ...@@ -666,17 +666,17 @@ class TestZodbPropertySheet(ERP5TypeTestCase):
Delete the given property from the test Property Sheet and check Delete the given property from the test Property Sheet and check
whether its corresponding accessor is not there anymore whether its corresponding accessor is not there anymore
""" """
import erp5.zodb_accessor_holder import erp5.accessor_holder
self.failIfHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.failIfHasAttribute(erp5.accessor_holder, 'TestMigration')
# Delete the property and force re-generation of TestMigration # Delete the property and force re-generation of TestMigration
# accessor holder # accessor holder
self.test_property_sheet.deleteContent(property_id) self.test_property_sheet.deleteContent(property_id)
self._forceTestAccessorHolderGeneration() self._forceTestAccessorHolderGeneration()
self.assertHasAttribute(erp5.zodb_accessor_holder, 'TestMigration') self.assertHasAttribute(erp5.accessor_holder, 'TestMigration')
self.failIfHasAttribute(erp5.zodb_accessor_holder.TestMigration, self.failIfHasAttribute(erp5.accessor_holder.TestMigration,
accessor_name) accessor_name)
def testDeleteStandardPropertyFromZodbPropertySheet(self): def testDeleteStandardPropertyFromZodbPropertySheet(self):
......
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