From a8edbd2e8d4cd6254531bcfaf7ae480338249738 Mon Sep 17 00:00:00 2001
From: Gorka Eguileor <geguileo@redhat.com>
Date: Thu, 7 Jul 2016 13:14:40 +0200
Subject: [PATCH] Improve api_version decorator to avoid noqa

Our existing `api_version` decorator for microversions forces us to use
"# noqa" if we have multiple microversions of a method in the same
class, providing no way around this.

This patch adds a way to avoid using noqa directive.  Previously we
would have:

 @wsgi.Controller.api_version("3.3")
 def my_api_method(self, req, id):
     .... method_1 ...

 @wsgi.Controller.api_version("3.4")  # noqa
 def my_api_method(self, req, id):
     .... method_2 ...

With this patch the second method does not require noqa anymore:

 @my_api_method.api_version("3.4")
 def my_api_method(self, req, id):
     .... method_2 ...

Devref is updated to reflect this new change and to mention that it is
the recommended way to do it.

Change-Id: I46283b52cc6a347d5deb0f57a123eba4e01a3eb2
Closes-Bug: #1599806
---
 cinder/api/openstack/wsgi.py               |  6 ++++++
 cinder/api/versions.py                     |  4 ++--
 cinder/tests/unit/api/test_versions.py     | 11 +++++++----
 doc/source/devref/api_microversion_dev.rst | 15 ++++++++++-----
 4 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/cinder/api/openstack/wsgi.py b/cinder/api/openstack/wsgi.py
index cc4718e55fd..76d92f21ece 100644
--- a/cinder/api/openstack/wsgi.py
+++ b/cinder/api/openstack/wsgi.py
@@ -1220,6 +1220,12 @@ class Controller(object):
             # ranges of valid versions as that is ambiguous
             func_list.sort(reverse=True)
 
+            # NOTE(geguileo): To avoid PEP8 errors when defining multiple
+            # microversions of the same method in the same class we add the
+            # api_version decorator to the function so it can be used instead,
+            # thus preventing method redefinition errors.
+            f.api_version = cls.api_version
+
             return f
 
         return decorator
diff --git a/cinder/api/versions.py b/cinder/api/versions.py
index 60413d3b43a..d6acf214ce9 100644
--- a/cinder/api/versions.py
+++ b/cinder/api/versions.py
@@ -100,7 +100,7 @@ class VersionsController(wsgi.Controller):
         known_versions.pop('v3.0')
         return builder.build_versions(known_versions)
 
-    @wsgi.Controller.api_version('2.0')  # noqa
+    @index.api_version('2.0')
     def index(self, req):  # pylint: disable=E0102
         """Return versions supported prior to the microversions epoch."""
         builder = views_versions.get_view_builder(req)
@@ -109,7 +109,7 @@ class VersionsController(wsgi.Controller):
         known_versions.pop('v3.0')
         return builder.build_versions(known_versions)
 
-    @wsgi.Controller.api_version('3.0')  # noqa
+    @index.api_version('3.0')
     def index(self, req):  # pylint: disable=E0102
         """Return versions supported after the start of microversions."""
         builder = views_versions.get_view_builder(req)
diff --git a/cinder/tests/unit/api/test_versions.py b/cinder/tests/unit/api/test_versions.py
index 1d3dc9c842a..d254de1db50 100644
--- a/cinder/tests/unit/api/test_versions.py
+++ b/cinder/tests/unit/api/test_versions.py
@@ -262,7 +262,8 @@ class VersionsControllerTestCase(test.TestCase):
         ('3.2', 'index', 'child 3.2', 'ControllerChild'),
         ('3.2', 'show', 404, 'ControllerChild'),
         ('3.3', 'index', 'child 3.3', 'ControllerChild'),
-        ('3.3', 'show', 'show', 'ControllerChild'))
+        ('3.3', 'show', 'show', 'ControllerChild'),
+        ('3.4', 'index', 'child 3.4', 'ControllerChild'))
     @ddt.unpack
     def test_versions_inheritance_of_non_base_controller(self, version, call,
                                                          expected, controller):
@@ -287,12 +288,14 @@ class VersionsControllerTestCase(test.TestCase):
             def index(self, req):
                 return 'child 3.2'
 
-            # TODO(geguileo): Figure out a way to make microversions work in a
-            # way that doesn't raise complaints from duplicated method.
-            @wsgi.Controller.api_version('3.3')  # noqa
+            @index.api_version('3.3')
             def index(self, req):
                 return 'child 3.3'
 
+            @index.api_version('3.4')
+            def index(self, req):
+                return 'child 3.4'
+
             @wsgi.Controller.api_version('3.3')
             def show(self, req, *args, **kwargs):
                 return 'show'
diff --git a/doc/source/devref/api_microversion_dev.rst b/doc/source/devref/api_microversion_dev.rst
index 9f895682909..5adab6ccb6b 100644
--- a/doc/source/devref/api_microversion_dev.rst
+++ b/doc/source/devref/api_microversion_dev.rst
@@ -186,7 +186,7 @@ In the controller class::
     def my_api_method(self, req, id):
         .... method_1 ...
 
-    @wsgi.Controller.api_version("3.4")  # noqa
+    @my_api_method.api_version("3.4")
     def my_api_method(self, req, id):
         .... method_2 ...
 
@@ -194,10 +194,15 @@ If a caller specified ``3.1``, ``3.2`` or ``3.3`` (or received the
 default of ``3.1``) they would see the result from ``method_1``,
 ``3.4`` or later ``method_2``.
 
-It is vital that the two methods have the same name, so the second of
-them will need ``# noqa`` to avoid failing flake8's ``F811`` rule. The
-two methods may be different in any kind of semantics (schema
-validation, return values, response codes, etc)
+We could use ``wsgi.Controller.api_version`` decorator on the second
+``my_api_method`` as well, but then we would have to add ``# no qa`` to that
+line to avoid failing flake8's ``F811`` rule.  So the recommended approach is
+to use the ``api_version`` decorator from the first method that is defined, as
+illustrated by the example above, and then use ``my_api_method`` decorator for
+subsequent api versions of the same method.
+
+The two methods may be different in any kind of semantics (schema validation,
+return values, response codes, etc.).
 
 A method with only small changes between versions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~