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): ...@@ -115,7 +115,8 @@ class ComponentTool(BaseTool):
security.declareProtected(Permissions.ResetDynamicClasses, 'reset') security.declareProtected(Permissions.ResetDynamicClasses, 'reset')
def reset(self, def reset(self,
force=False, 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 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 the reset is necessary when force is not specified. This allows to make
...@@ -152,10 +153,15 @@ class ComponentTool(BaseTool): ...@@ -152,10 +153,15 @@ class ComponentTool(BaseTool):
from Products.ERP5Type.dynamic.component_package import ComponentDynamicPackage from Products.ERP5Type.dynamic.component_package import ComponentDynamicPackage
with aq_method_lock: with aq_method_lock:
component_package_list = [] component_package_list = []
for package in six.itervalues(erp5.component.__dict__): if only_test_component_module:
if isinstance(package, ComponentDynamicPackage): package_list = (erp5.component.test, )
package.reset() else:
component_package_list.append(package.__name__) 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.filesystem_import_dict = None
erp5.component.ref_manager.gc() erp5.component.ref_manager.gc()
...@@ -166,7 +172,8 @@ class ComponentTool(BaseTool): ...@@ -166,7 +172,8 @@ class ComponentTool(BaseTool):
for k in list(astroid_cache.keys()): for k in list(astroid_cache.keys()):
if k.startswith('erp5.component.') and k not in component_package_list: if k.startswith('erp5.component.') and k not in component_package_list:
del astroid_cache[k] del astroid_cache[k]
if only_test_component_module:
return True
if reset_portal_type_at_transaction_boundary: if reset_portal_type_at_transaction_boundary:
portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary() portal.portal_types.resetDynamicDocumentsOnceAtTransactionBoundary()
else: else:
...@@ -177,19 +184,31 @@ class ComponentTool(BaseTool): ...@@ -177,19 +184,31 @@ class ComponentTool(BaseTool):
security.declareProtected(Permissions.ResetDynamicClasses, security.declareProtected(Permissions.ResetDynamicClasses,
'resetOnceAtTransactionBoundary') '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 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 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 often as possible. Moreover, doing it twice in a transaction is useless
(but still twice as costly). (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() tv = getTransactionalVariable()
key = 'ComponentTool.resetOnceAtTransactionBoundary' key = 'ComponentTool.resetOnceAtTransactionBoundary'
if key not in tv: if key not in tv:
tv[key] = None tv[key] = None
transaction.get().addBeforeCommitHook(self.reset, transaction.get().addBeforeCommitHook(
args=(True, True)) self.reset,
kws={
'force': True,
'reset_portal_type_at_transaction_boundary': True,
'only_test_component_module': only_test_component_module
}
)
__test_text_content_template = '''\ __test_text_content_template = '''\
############################################################################## ##############################################################################
......
...@@ -3400,6 +3400,17 @@ break_at_import() ...@@ -3400,6 +3400,17 @@ break_at_import()
expected_output = "ModuleNotFoundError: No module named 'testDoesNotExist_import_error_because_module_does_not_exist'" expected_output = "ModuleNotFoundError: No module named 'testDoesNotExist_import_error_because_module_does_not_exist'"
self.assertIn(expected_output, output) 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): def testERP5Broken(self):
# Create a broken ghost object # Create a broken ghost object
import erp5.portal_type 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