From: Roman Podoliaka Date: Wed, 16 Apr 2014 10:59:33 +0000 (+0300) Subject: Fix issubclass() hook behavior in PluginInterface X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=237f05839668d8d26e81b02746086c651431988b;p=openstack-build%2Fneutron-build.git Fix issubclass() hook behavior in PluginInterface Currently, PluginInterface provides an issubclass() hook that returns True for issubclass(A, B) call, if all abstract methods of B (stored in B.__abstractmethods__) can be found in the A.__mro__ tuple of classes. But there is an edge case, when B doesn't have any abstract methods, which leads to issubclass(A, B) call returning True even if A and B are not related all. E.g. issubclass(NeutronPluginPLUMgridV2, NsxPlugin) returns True, while these two are different core plugins. And it gets even more trickier when superclasses are involved: e.g. SecurityGroupDbMixin is a superclass of NsxPlugin, so depending on the fact whether the python module with NsxPlugin class is imported or not, issubclass(NeutronPluginPLUMgridV2, SecurityGroupDbMixin) will return either False or True accordingly. Closes-Bug: #1308489 Change-Id: I92711a00a19b89729ccdba9cbd8a2e7a2d2868ed --- diff --git a/neutron/api/extensions.py b/neutron/api/extensions.py index 666b2cede..265249323 100644 --- a/neutron/api/extensions.py +++ b/neutron/api/extensions.py @@ -51,6 +51,10 @@ class PluginInterface(object): marked with the abstractmethod decorator is provided by the plugin class. """ + + if not cls.__abstractmethods__: + return NotImplemented + for method in cls.__abstractmethods__: if any(method in base.__dict__ for base in klass.__mro__): continue diff --git a/neutron/tests/unit/test_extensions.py b/neutron/tests/unit/test_extensions.py index fc373709b..6ab310f75 100644 --- a/neutron/tests/unit/test_extensions.py +++ b/neutron/tests/unit/test_extensions.py @@ -15,6 +15,8 @@ # License for the specific language governing permissions and limitations # under the License. +import abc + import mock import routes import webob @@ -57,6 +59,47 @@ class FakePluginWithExtension(db_base_plugin_v2.NeutronDbPluginV2): self._log("method_to_support_foxnsox_extension", context) +class PluginInterfaceTest(base.BaseTestCase): + def test_issubclass_hook(self): + class A(object): + def f(self): + pass + + class B(extensions.PluginInterface): + @abc.abstractmethod + def f(self): + pass + + self.assertTrue(issubclass(A, B)) + + def test_issubclass_hook_class_without_abstract_methods(self): + class A(object): + def f(self): + pass + + class B(extensions.PluginInterface): + def f(self): + pass + + self.assertFalse(issubclass(A, B)) + + def test_issubclass_hook_not_all_methods_implemented(self): + class A(object): + def f(self): + pass + + class B(extensions.PluginInterface): + @abc.abstractmethod + def f(self): + pass + + @abc.abstractmethod + def g(self): + pass + + self.assertFalse(issubclass(A, B)) + + class ResourceExtensionTest(base.BaseTestCase): class ResourceExtensionController(wsgi.Controller):