diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py index 5e05371419081be52f66f3ce24f5cf1a602133e7..c7877486650ec223897e87d7a0cef57f93b92e93 100644 --- a/product/ERP5/Document/BusinessTemplate.py +++ b/product/ERP5/Document/BusinessTemplate.py @@ -4234,7 +4234,7 @@ class DocumentTemplateItem(FilesystemToZodbTemplateItem): @staticmethod def _getZodbObjectId(id): - return DocumentComponent._getIdPrefix() + '.' + id + return DocumentComponent.getIdPrefix() + '.' + id @staticmethod def _getFilesystemPath(class_id): @@ -4342,7 +4342,7 @@ class ExtensionTemplateItem(DocumentTemplateItem): @staticmethod def _getZodbObjectId(id): - return ExtensionComponent._getIdPrefix() + '.' + id + return ExtensionComponent.getIdPrefix() + '.' + id def getTemplateIdList(self): return self.getTemplateExtensionIdList() @@ -4363,7 +4363,7 @@ class TestTemplateItem(DocumentTemplateItem): @staticmethod def _getZodbObjectId(id): - return TestComponent._getIdPrefix() + '.' + id + return TestComponent.getIdPrefix() + '.' + id def getTemplateIdList(self): return self.getTemplateTestIdList() diff --git a/product/ERP5/tests/testBusinessTemplate.py b/product/ERP5/tests/testBusinessTemplate.py index 9d1f64dec079a8c892578f78d2ba973f55840a0a..4c954972fcbac982fe329b553b85538208491c4f 100644 --- a/product/ERP5/tests/testBusinessTemplate.py +++ b/product/ERP5/tests/testBusinessTemplate.py @@ -7349,7 +7349,7 @@ class TestBusinessTemplate(BusinessTemplateMixin): Folder._setOb(self, id, object) """) - component_id_prefix = DocumentComponent._getIdPrefix() + component_id_prefix = DocumentComponent.getIdPrefix() component_portal_type = DocumentComponent.portal_type tool_type = 'My Tool' tool_class = 'MyTool' @@ -7859,7 +7859,7 @@ class TestDocumentTemplateItem(BusinessTemplateMixin): sequence_list.addSequenceString(sequence_string) sequence_list.play(self) - component_id_prefix = DocumentComponent._getIdPrefix() + component_id_prefix = DocumentComponent.getIdPrefix() component_portal_type = DocumentComponent.portal_type def stepCreateZodbDocument(self, sequence=None, **kw): @@ -8345,7 +8345,7 @@ class TestExtensionTemplateItem(TestDocumentTemplateItem): template_property = 'template_extension_id_list' # Specific to ZODB Extension Component - component_id_prefix = ExtensionComponent._getIdPrefix() + component_id_prefix = ExtensionComponent.getIdPrefix() component_portal_type = ExtensionComponent.portal_type from Products.ERP5Type.Core.TestComponent import TestComponent @@ -8364,7 +8364,7 @@ class TestTestTemplateItem(TestDocumentTemplateItem): template_property = 'template_test_id_list' # Specific to ZODB Extension Component - component_id_prefix = TestComponent._getIdPrefix() + component_id_prefix = TestComponent.getIdPrefix() component_portal_type = TestComponent.portal_type def stepAddTestToBusinessTemplate(self, sequence=None, **kw): diff --git a/product/ERP5Type/Core/DocumentComponent.py b/product/ERP5Type/Core/DocumentComponent.py index 55e21ba189953d75bf36949c132e6b8a18998254..e6b5b9e3b4ae0a8fa17d0a8a236d0b6e0962d133 100644 --- a/product/ERP5Type/Core/DocumentComponent.py +++ b/product/ERP5Type/Core/DocumentComponent.py @@ -60,5 +60,5 @@ class DocumentComponent(ComponentMixin, TextContentHistoryMixin): return 'erp5.component.document' @staticmethod - def _getIdPrefix(): + def getIdPrefix(): return 'document' diff --git a/product/ERP5Type/Core/ExtensionComponent.py b/product/ERP5Type/Core/ExtensionComponent.py index a448499161fe6cd4b0893c1a0b565711f05440e0..ce6540cc6a3b15448f81333f64561cc29480b751 100644 --- a/product/ERP5Type/Core/ExtensionComponent.py +++ b/product/ERP5Type/Core/ExtensionComponent.py @@ -59,5 +59,5 @@ class ExtensionComponent(ComponentMixin, TextContentHistoryMixin): return 'erp5.component.extension' @staticmethod - def _getIdPrefix(): + def getIdPrefix(): return 'extension' diff --git a/product/ERP5Type/Core/TestComponent.py b/product/ERP5Type/Core/TestComponent.py index 733a1115e7dd784f5b30b5e3a309f824f1803487..997489106a7c5ce6910c48911602c46911745644 100644 --- a/product/ERP5Type/Core/TestComponent.py +++ b/product/ERP5Type/Core/TestComponent.py @@ -60,5 +60,5 @@ class TestComponent(ComponentMixin, TextContentHistoryMixin): return 'erp5.component.test' @staticmethod - def _getIdPrefix(): + def getIdPrefix(): return 'test' diff --git a/product/ERP5Type/Tool/TypesTool.py b/product/ERP5Type/Tool/TypesTool.py index 6e8c53eddcdbaabcdfdd2a6367e9f23747bae3a4..178cbe7411cc8e08abe36e612535f325403c7095 100644 --- a/product/ERP5Type/Tool/TypesTool.py +++ b/product/ERP5Type/Tool/TypesTool.py @@ -210,7 +210,23 @@ class TypesTool(TypeProvider): document_type_set = set(document_class_registry) import erp5.component.document - document_type_set.update(erp5.component.document._registry_dict) + portal = self.getPortalObject() + version_priority_set = set(portal.getVersionPriorityNameList()) + + # objectValues should not be used for a large number of objects, but + # this is only done upon reset, moreover using the Catalog is too risky + # as it lags behind and depends upon objects being reindexed + for component in portal.portal_components.objectValues(portal_type='Document Component'): + # Only consider modified or validated states as state transition will + # be handled by component_validation_workflow which will take care of + # updating the registry + validation_state_tuple = component.getValidationState() + if validation_state_tuple in ('modified', 'validated'): + version = component.getVersion(validated_only=True) + # The versions should have always been set on ERP5Site property + # beforehand + if version in version_priority_set: + document_type_set.add(component.getReference(validated_only=True)) return sorted(document_type_set) diff --git a/product/ERP5Type/dynamic/component_package.py b/product/ERP5Type/dynamic/component_package.py index a5dee3b07ad354a63c178296bf1120e969bf5b54..e36046ae5c3ee05a36f92a8be2126137a6e022d4 100644 --- a/product/ERP5Type/dynamic/component_package.py +++ b/product/ERP5Type/dynamic/component_package.py @@ -39,6 +39,7 @@ from Products.ERP5Type.Globals import get_request from . import aq_method_lock from types import ModuleType from zLOG import LOG, BLATHER, WARNING +from Acquisition import aq_base class ComponentVersionPackage(ModuleType): """ @@ -73,9 +74,9 @@ class ComponentDynamicPackage(ModuleType): self._namespace = namespace self._namespace_prefix = namespace + '.' + self._id_prefix = namespace.rsplit('.', 1)[1] self._portal_type = portal_type self.__version_suffix_len = len('_version') - self.__registry_dict = collections.defaultdict(dict) self.__fullname_source_code_dict = {} # Add this module to sys.path for future imports @@ -84,48 +85,6 @@ class ComponentDynamicPackage(ModuleType): # Add the import hook sys.meta_path.append(self) - @property - def _registry_dict(self): - """ - Create the component registry, this is very similar to - Products.ERP5Type.document_class_registry and avoids checking whether a - Component exists at each call at the expense of being slower when being - re-generated after a reset. Moreover, it allows to handle reference - easily. - """ - if not self.__registry_dict: - portal = getSite() - - try: - component_tool = portal.portal_components - # When installing ERP5 site, erp5_core_components has not been installed - # yet, thus this will obviously failed... - # - # XXX-arnau: Is this needed as it is now done in synchronizeDynamicModules? - except AttributeError: - return {} - - version_priority_set = set(portal.getVersionPriorityNameList()) - - # objectValues should not be used for a large number of objects, but - # this is only done upon reset, moreover using the Catalog is too risky - # as it lags behind and depends upon objects being reindexed - for component in component_tool.objectValues(portal_type=self._portal_type): - # Only consider modified or validated states as state transition will - # be handled by component_validation_workflow which will take care of - # updating the registry - validation_state_tuple = component.getValidationState() - if validation_state_tuple in ('modified', 'validated'): - version = component.getVersion(validated_only=True) - # The versions should have always been set on ERP5Site property - # beforehand - if version in version_priority_set: - reference = component.getReference(validated_only=True) - self.__registry_dict[reference][version] = (component.getId(), - component._p_oid) - - return self.__registry_dict - def get_source(self, fullname): """ PEP-302 function to get the source code, used mainly by linecache for @@ -160,12 +119,6 @@ class ComponentDynamicPackage(ModuleType): except RuntimeError: import_lock_held = False - # The import lock has been released, but as _registry_dict may be - # initialized or cleared, no other Components should access this critical - # region - # - # TODO-arnau: Too coarse-grain? - aq_method_lock.acquire() try: site = getSite() @@ -173,6 +126,7 @@ class ComponentDynamicPackage(ModuleType): # erp5.component.XXX.YYY.ZZZ where erp5.component.XXX.YYY is the current # Component where an import is done name = fullname[len(self._namespace_prefix):] + # name=VERSION_version.REFERENCE if '.' in name: try: version, name = name.split('.') @@ -180,23 +134,38 @@ class ComponentDynamicPackage(ModuleType): except ValueError: return None - try: - self._registry_dict[name][version] - except KeyError: + id_ = "%s.%s.%s" % (self._id_prefix, version, name) + # aq_base() because this should not go up to ERP5Site and trigger + # side-effects, after all this only check for existence... + component = getattr(aq_base(site.portal_components), id_, None) + if component is None or component.getValidationState() not in ('modified', + 'validated'): return None # Skip unavailable components, otherwise Products for example could be # wrongly considered as importable and thus the actual filesystem class # ignored - elif (name not in self._registry_dict and - name[:-self.__version_suffix_len] not in site.getVersionPriorityNameList()): - return None + # + # name=VERSION_version + elif name.endswith('_version'): + if name[:-self.__version_suffix_len] not in site.getVersionPriorityNameList(): + return None + + # name=REFERENCE + else: + component_tool = aq_base(site.portal_components) + for version in site.getVersionPriorityNameList(): + id_ = "%s.%s.%s" % (self._id_prefix, version, name) + component = getattr(component_tool, id_, None) + if component is not None and component.getValidationState() in ('modified', + 'validated'): + break + else: + return None return self finally: - aq_method_lock.release() - # Internal release of import lock at the end of import machinery will # fail if the hook is not acquired if import_lock_held: @@ -258,25 +227,17 @@ class ComponentDynamicPackage(ModuleType): raise ImportError("%s: should be %s.VERSION.COMPONENT_REFERENCE (%s)" % \ (fullname, self._namespace, error)) - try: - component_id = self._registry_dict[name][version][0] - except KeyError: - raise ImportError("%s: version %s of Component %s could not be found" % \ - (fullname, version, name)) + component_id = "%s.%s.%s" % (self._id_prefix, version, name) # Otherwise, find the Component with the highest version priority else: - try: - component_version_dict = self._registry_dict[name] - except KeyError: - raise ImportError("%s: Component %s could not be found" % (fullname, - name)) - + component_tool = aq_base(site.portal_components) # Version priority name list is ordered in descending order for version in site.getVersionPriorityNameList(): - component_id_uid_tuple = component_version_dict.get(version) - if component_id_uid_tuple is not None: - component_id = component_id_uid_tuple[0] + component_id = "%s.%s.%s" % (self._id_prefix, version, name) + component = getattr(component_tool, component_id, None) + if component is not None and component.getValidationState() in ('modified', + 'validated'): break else: raise ImportError("%s: no version of Component %s in Site priority" % \ @@ -433,8 +394,7 @@ class ComponentDynamicPackage(ModuleType): if sub_package: package = sub_package else: - # Clear the Component registry and source code dict only once - self.__registry_dict.clear() + # Clear the source code dict only once self.__fullname_source_code_dict.clear() package = self diff --git a/product/ERP5Type/interfaces/component.py b/product/ERP5Type/interfaces/component.py index b1eda3dcc14c22d74ea451b2a0dee2d00008e6b8..aecd9a9f929483afaeaa96687df468b9fa3909e8 100644 --- a/product/ERP5Type/interfaces/component.py +++ b/product/ERP5Type/interfaces/component.py @@ -63,7 +63,7 @@ class IComponent(Interface): Return the module name where Component module are loaded into """ - def _getIdPrefix(): + def getIdPrefix(): """ Return the ID prefix for Component objects """ diff --git a/product/ERP5Type/mixin/component.py b/product/ERP5Type/mixin/component.py index d6826cac71bbd8b05b448bbb55142577c683ddbf..d1782c11c3152c07e79607f3f0f4b9b895ee0c45 100644 --- a/product/ERP5Type/mixin/component.py +++ b/product/ERP5Type/mixin/component.py @@ -181,14 +181,14 @@ class ComponentMixin(PropertyRecordableMixin, Base): default='') } + _message_invalid_id = "ID is invalid, should be '${id_prefix}.VERSION.REFERENCE'" + _message_reference_not_set = "Reference must be set" _message_invalid_reference = "Reference cannot end with '_version' or "\ "start with '_' or be equal to find_module, load_module or reset" _message_version_not_set = "Version must be set" _message_invalid_version = "Version cannot start with '_'" - _message_duplicated_version_reference = "${id} is validated has the same "\ - "Reference and Version" _message_text_content_not_set = "No source code" _message_text_content_error = "Error in Source Code: ${error_message}" @@ -220,10 +220,27 @@ class ComponentMixin(PropertyRecordableMixin, Base): """ error_list = super(ComponentMixin, self).checkConsistency(*args, **kw) object_relative_url = self.getRelativeUrl() + + is_id_invalid = False + try: + prefix, version, reference = self.getId().split('.') + except ValueError: + is_id_invalid = True + else: + if (prefix != self.getIdPrefix() or + version != self.getVersion() or + reference != self.getReference()): + is_id_invalid = True + + if is_id_invalid: + error_list.append( + ConsistencyMessage(self, + object_relative_url, + message=self._message_invalid_id, + mapping={'id_prefix': self.getIdPrefix()})) + reference = self.getReference() - reference_has_error = False if not reference: - reference_has_error = True error_list.append( ConsistencyMessage(self, object_relative_url, @@ -233,7 +250,6 @@ class ComponentMixin(PropertyRecordableMixin, Base): elif (reference.endswith('_version') or reference[0] == '_' or reference in ('find_module', 'load_module', 'reset')): - reference_has_error = True error_list.append( ConsistencyMessage(self, object_relative_url, @@ -251,26 +267,6 @@ class ComponentMixin(PropertyRecordableMixin, Base): object_relative_url, message=self._message_invalid_version, mapping={})) - else: - package = __import__(self._getDynamicModuleNamespace(), globals(), - fromlist=[self._getDynamicModuleNamespace()], level=0) - component_id = None - component_uid = None - from Products.ERP5Type.dynamic import aq_method_lock - with aq_method_lock: - component_id_uid_tuple = package._registry_dict.get( - self.getReference(), {}).get(self.getVersion(), None) - if component_id_uid_tuple: - component_id, component_uid = component_id_uid_tuple - - if (component_id is not None and component_uid is not None and - not reference_has_error and - component_uid != self._p_oid and component_id != self.getId()): - error_list.append( - ConsistencyMessage(self, - object_relative_url, - message=self._message_duplicated_version_reference, - mapping=dict(id=component_id))) text_content = self.getTextContent() if not text_content: @@ -364,7 +360,7 @@ class ComponentMixin(PropertyRecordableMixin, Base): # needed when importing from filesystem, moreover errors may occur # if in the same transaction a Component is created and another # one depending upon the former... - object_id = '%s.%s.%s' % (cls._getIdPrefix(), version, reference) + object_id = '%s.%s.%s' % (cls.getIdPrefix(), version, reference) new_component = context.newContent(id=object_id, reference=reference, version=version, diff --git a/product/ERP5Type/tests/testDynamicClassGeneration.py b/product/ERP5Type/tests/testDynamicClassGeneration.py index 7fca995cfc367f5cbfbdc3ec71538b5ef5ea07a8..c9315e5a33abdbb9c7dfd007850e3e73bcda2d5e 100644 --- a/product/ERP5Type/tests/testDynamicClassGeneration.py +++ b/product/ERP5Type/tests/testDynamicClassGeneration.py @@ -1316,7 +1316,7 @@ class _TestZodbComponent(SecurityTestCase): def afterSetUp(self): self._component_tool = self.portal.portal_components - self._module = __import__(self._getComponentModuleName(), + self._module = __import__(self._document_class._getDynamicModuleNamespace(), fromlist=['erp5.component']) self._component_tool.reset(force=True, reset_portal_type_at_transaction_boundary=True) @@ -1325,28 +1325,18 @@ class _TestZodbComponent(SecurityTestCase): """ Create new Component """ - full_id = '%s.%s.%s' % (self._getComponentModuleName(), - version + '_version', - reference) - - if id_ is not None: - full_id += '.%s' % id_ + if id_ is None: + id_ = '%s.%s.%s' % (self._document_class.getIdPrefix(), version, reference) return self._component_tool.newContent( - id=full_id, + id=id_, version=version, reference=reference, text_content=text_content, - portal_type=self._component_portal_type) - - @abc.abstractmethod - def _getComponentModuleName(self): - """ - Abstract method defining ZODB Component top-level package name - """ + portal_type=self._portal_type) def _getComponentFullModuleName(self, module_name): - return self._getComponentModuleName() + '.' + module_name + return self._document_class._getDynamicModuleNamespace() + '.' + module_name def failIfModuleImportable(self, module_name): """ @@ -1380,7 +1370,7 @@ class _TestZodbComponent(SecurityTestCase): self._getComponentFullModuleName(module_name)) if expected_default_version is not None: - top_module_name = self._getComponentModuleName() + top_module_name = self._document_class._getDynamicModuleNamespace() top_module = __import__(top_module_name, level=0, fromlist=[top_module_name]) # The module must be available in its default version @@ -1410,7 +1400,7 @@ class _TestZodbComponent(SecurityTestCase): def _importModule(self, module_name): return __import__(self._getComponentFullModuleName(module_name), - fromlist=[self._getComponentModuleName()], + fromlist=[self._document_class._getDynamicModuleNamespace()], level=0) def testValidateInvalidateDelete(self): @@ -1492,6 +1482,37 @@ class _TestZodbComponent(SecurityTestCase): if o.getReference() == 'TestValidateInvalidateComponent'], []) + def testInvalidId(self): + """ + Check whether checkConsistency has been properly implemented for checking + Component ID which should follow the format 'getIdPrefix().VERSION.REFERENCE' + """ + id_prefix = self._document_class.getIdPrefix() + version = "erp5" + reference = "TestWithInvalidId" + + valid_id = "%s.%s.%s" % (id_prefix, version, reference) + component = self._newComponent(reference, + 'def foobar():\n return 42', + version, + valid_id) + self.tic() + self.assertEqual(component.checkConsistency(), []) + + for invalid_id in ("INVALID_PREFIX.%s.%s" % (version, reference), + "%s.INVALID_VERSION.%s" % (id_prefix, reference), + "%s.%s.INVALID_REFERENCE" % (id_prefix, version)): + component.setId(invalid_id) + self.tic() + self.assertEqual( + [m.getMessage().translate() for m in component.checkConsistency()], + [self.portal.Base_translateString(ComponentMixin._message_invalid_id, + mapping={'id_prefix': id_prefix})]) + + component.setId(valid_id) + self.tic() + self.assertEqual(component.checkConsistency(), []) + def testReferenceWithReservedKeywords(self): """ Check whether checkConsistency has been properly implemented for checking @@ -1537,6 +1558,10 @@ class _TestZodbComponent(SecurityTestCase): 'find_module': ComponentMixin._message_invalid_reference, 'load_module': ComponentMixin._message_invalid_reference} + invalid_id_error_message = self.portal.Base_translateString( + ComponentMixin._message_invalid_id, + mapping={'id_prefix': self._document_class.getIdPrefix()}) + for invalid_reference, error_message in invalid_reference_dict.iteritems(): # Reset should not be performed ComponentTool.reset = assertResetNotCalled @@ -1550,7 +1575,8 @@ class _TestZodbComponent(SecurityTestCase): self.assertEqual(component.getValidationState(), 'modified') self.assertEqual([m.getMessage().translate() for m in component.checkConsistency()], - [error_message]) + [invalid_id_error_message, + error_message]) self.assertEqual(component.getTextContentErrorMessageList(), []) self.assertEqual(component.getTextContentWarningMessageList(), []) self.assertEqual(component.getReference(), invalid_reference) @@ -1620,6 +1646,10 @@ class _TestZodbComponent(SecurityTestCase): # make sense to have reference starting with '_' '_TestVersionWithReservedKeywords': ComponentMixin._message_invalid_version} + invalid_id_error_message = self.portal.Base_translateString( + ComponentMixin._message_invalid_id, + mapping={'id_prefix': self._document_class.getIdPrefix()}) + for invalid_version, error_message in invalid_version_dict.iteritems(): # Reset should not be performed ComponentTool.reset = assertResetNotCalled @@ -1633,7 +1663,8 @@ class _TestZodbComponent(SecurityTestCase): self.assertEqual(component.getValidationState(), 'modified') self.assertEqual([m.getMessage().translate() for m in component.checkConsistency()], - [error_message]) + [invalid_id_error_message, + error_message]) self.assertEqual(component.getTextContentErrorMessageList(), []) self.assertEqual(component.getTextContentWarningMessageList(), []) self.assertEqual(component.getVersion(), invalid_version) @@ -1797,7 +1828,7 @@ class _TestZodbComponent(SecurityTestCase): component.validate() self.tic() - top_module_name = self._getComponentModuleName() + top_module_name = self._document_class._getDynamicModuleNamespace() # Create a new Component which uses a specific version of the previously # created Component @@ -1877,7 +1908,7 @@ def bar(*args, **kwargs): # added to ERP5Site version priorities self.failIfModuleImportable('foo_version.TestVersionPriority') - top_module_name = self._getComponentModuleName() + top_module_name = self._document_class._getDynamicModuleNamespace() top_module = __import__(top_module_name, level=0, fromlist=[top_module_name]) @@ -1963,45 +1994,6 @@ def bar(*args, **kwargs): self.assertUserCanModifyDocument(user_id, component) self.assertUserCanDeleteDocument(user_id, component) - def testValidateComponentWithSameReferenceVersionAlreadyValidated(self): - reference = 'ValidateComponentWithSameReferenceVersionAlreadyValidated' - - component = self._newComponent(reference, 'def foo():\n print "ok"') - component.validate() - self.tic() - - component_dup = self._newComponent(reference, 'def foo():\n print "ok"', - id_='duplicated') - - self.tic() - - from Products.DCWorkflow.DCWorkflow import ValidationFailed - self.assertRaises(ValidationFailed, - self.portal.portal_workflow.doActionFor, - component_dup, 'validate_action') - - self.assertEqual(component_dup.getValidationState(), 'draft') - - component_dup.setReference(reference + '_copy') - component_dup.validate() - self.tic() - - component_dup.setReference(reference) - self.tic() - self.assertEqual(component_dup.getValidationState(), 'modified') - self.assertEqual(component_dup.getReference(), reference) - self.assertEqual(component_dup.getReference(validated_only=True), - reference + '_copy') - - component_dup.invalidate() - self.tic() - component_dup.setReference(reference) - self.assertRaises(ValidationFailed, - self.portal.portal_workflow.doActionFor, - component_dup, 'validate_action') - - self.assertEqual(component_dup.getValidationState(), 'invalidated') - from Products.ERP5Type.Core.ExtensionComponent import ExtensionComponent class TestZodbExtensionComponent(_TestZodbComponent): @@ -2009,10 +2001,8 @@ class TestZodbExtensionComponent(_TestZodbComponent): Tests specific to ZODB Extension Component (previously defined in bt5 and installed on the filesystem in $INSTANCE_HOME/Extensions) """ - _component_portal_type = 'Extension Component' - - def _getComponentModuleName(self): - return ExtensionComponent._getDynamicModuleNamespace() + _portal_type = 'Extension Component' + _document_class = ExtensionComponent def testExternalMethod(self): """ @@ -2109,10 +2099,8 @@ class TestZodbDocumentComponent(_TestZodbComponent): previously defined in bt5 and installed on the filesystem in $INSTANCE_HOME/Document. Later on, Product Documents will also be migrated """ - _component_portal_type = 'Document Component' - - def _getComponentModuleName(self): - return DocumentComponent._getDynamicModuleNamespace() + _portal_type = 'Document Component' + _document_class = DocumentComponent def testAssignToPortalTypeClass(self): """ @@ -2240,10 +2228,8 @@ class TestZodbTestComponent(_TestZodbComponent): Tests specific to ZODB Test Component (known as Live Tests, and previously defined in bt5 and installed in $INSTANCE_HOME/test) """ - _component_portal_type = 'Test Component' - - def _getComponentModuleName(self): - return TestComponent._getDynamicModuleNamespace() + _portal_type = 'Test Component' + _document_class = TestComponent def testRunLiveTest(self): """