Commit c614f84c authored by Jérome Perrin's avatar Jérome Perrin

ComponentTool: faster reload of test components

When editing a test component, only reload test component module, no
need to reload all components modules because other modules do not
depend on test module.
parent ec16af67
Pipeline #40216 failed with stage
in 0 seconds
state_change['object'].getPortalObject().portal_components.resetOnceAtTransactionBoundary()
component = state_change['object']
component.getPortalObject().portal_components.resetOnceAtTransactionBoundary(
only_test_component_module=component.getPortalType() == 'Test Component'
)
......@@ -115,7 +115,8 @@ class ComponentTool(BaseTool):
security.declareProtected(Permissions.ResetDynamicClasses, 'reset')
def reset(self,
force=False,
reset_portal_type_at_transaction_boundary=False):
reset_portal_type_at_transaction_boundary=False,
only_test_component_module=None):
"""
Reset all ZODB Component packages. A cache cookie is used to check whether
the reset is necessary when force is not specified. This allows to make
......@@ -152,10 +153,15 @@ class ComponentTool(BaseTool):
from Products.ERP5Type.dynamic.component_package import ComponentDynamicPackage
with aq_method_lock:
component_package_list = []
for package in six.itervalues(erp5.component.__dict__):
if isinstance(package, ComponentDynamicPackage):
package.reset()
component_package_list.append(package.__name__)
if only_test_component_module:
package_list = (erp5.component.test, )
else:
package_list = [
package for package in six.itervalues(erp5.component.__dict__)
if isinstance(package, ComponentDynamicPackage)]
for package in package_list:
package.reset()
component_package_list.append(package.__name__)
erp5.component.filesystem_import_dict = None
erp5.component.ref_manager.gc()
......@@ -166,7 +172,8 @@ class ComponentTool(BaseTool):
for k in list(astroid_cache.keys()):
if k.startswith('erp5.component.') and k not in component_package_list:
del astroid_cache[k]
if only_test_component_module:
return True
if reset_portal_type_at_transaction_boundary:
portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
else:
......@@ -177,19 +184,31 @@ class ComponentTool(BaseTool):
security.declareProtected(Permissions.ResetDynamicClasses,
'resetOnceAtTransactionBoundary')
def resetOnceAtTransactionBoundary(self):
def resetOnceAtTransactionBoundary(self, only_test_component_module=False):
"""
Schedule a single reset at the end of the transaction. The idea behind
this is that a reset is (very) costly and that we want to do it as little
often as possible. Moreover, doing it twice in a transaction is useless
(but still twice as costly).
Test component module is a bit different, because test components are
not involved in dynamic classes, we don't need to rebuilt the portal type
classes when editing a test component. For this reason, this method
supports a `only_test_component_module` argument to only reload the module.
"""
tv = getTransactionalVariable()
key = 'ComponentTool.resetOnceAtTransactionBoundary'
if key not in tv:
tv[key] = None
transaction.get().addBeforeCommitHook(self.reset,
args=(True, True))
transaction.get().addBeforeCommitHook(
self.reset,
kws={
'force': True,
'reset_portal_type_at_transaction_boundary': True,
'only_test_component_module': only_test_component_module
}
)
__test_text_content_template = '''\
##############################################################################
......
......@@ -3400,6 +3400,17 @@ break_at_import()
expected_output = "ModuleNotFoundError: No module named 'testDoesNotExist_import_error_because_module_does_not_exist'"
self.assertIn(expected_output, output)
def test_dynamic_modules_not_reloaded_on_test_component_edit(self):
source_code = self._getValidSourceCode()
component = self._newComponent('testNoPortalTypeClassReload', source_code)
component.validate()
self.tic()
from erp5.component.document.erp5_version.Person import Person as class_before
component.setTextContent(source_code + '\n#change\n')
self.tic()
from erp5.component.document.erp5_version.Person import Person as class_after
self.assertIs(class_after, class_before)
def testERP5Broken(self):
# Create a broken ghost object
import erp5.portal_type
......
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