Add hacking rule for api_version

As an implementation limitation, the 'api_version' decorator must be the
first decorator if applied on a method. This patch adds a pep8 check for
this to be enforced.

partial-bp: api-microversioning
Change-Id: Ib03ad91e492175d23dde8b3a30dae9a077baffb9
This commit is contained in:
tengqm 2016-03-28 23:21:46 -04:00
parent e0e5250d57
commit 9742996102
3 changed files with 63 additions and 0 deletions

View File

@ -13,6 +13,7 @@ Senlin Specific Commandments
- [S319] Use ``jsonutils`` functions rather than using the ``json`` package
directly.
- [S320] Default arguments of a method should not be mutable.
- [S321] The api_version decorator has to be the first decorator on a method.
Working on APIs
---------------

View File

@ -15,6 +15,8 @@ import re
asse_equal_end_with_none_re = re.compile(r"assertEqual\(.*?,\s+None\)$")
asse_equal_start_with_none_re = re.compile(r"assertEqual\(None,")
mutable_default_args = re.compile(r"^\s*def .+\((.+=\{\}|.+=\[\])")
api_version_dec = re.compile(r"@.*api_version")
decorator_re = re.compile(r"@.*")
def assert_equal_none(logical_line):
@ -46,7 +48,17 @@ def no_mutable_default_args(logical_line):
yield (0, msg)
def check_api_version_decorator(logical_line, previous_logical, blank_before,
filename):
msg = ("S321: The api_version decorator must be the first decorator on "
"a method.")
if (blank_before == 0 and re.match(api_version_dec, logical_line) and
re.match(decorator_re, previous_logical)):
yield(0, msg)
def factory(register):
register(assert_equal_none)
register(use_jsonutils)
register(no_mutable_default_args)
register(check_api_version_decorator)

View File

@ -10,6 +10,10 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
import pep8
import textwrap
from senlin.hacking import checks
from senlin.tests.unit.common import base
@ -48,3 +52,49 @@ class HackingTestCase(base.SenlinTestCase):
self.assertEqual(0, len(list(checks.no_mutable_default_args(
"defined, undefined = [], {}"))))
@mock.patch("pep8._checks",
{'physical_line': {}, 'logical_line': {}, 'tree': {}})
def test_api_version_decorator(self):
code = """
@some_other_decorator
@wsgi.api_version("2.2")
def my_method():
pass
"""
lines = textwrap.dedent(code).strip().splitlines(True)
pep8.register_check(checks.check_api_version_decorator)
checker = pep8.Checker(filename=None, lines=lines)
checker.check_all()
checker.report._deferred_print.sort()
actual_error = checker.report._deferred_print[0]
self.assertEqual(2, actual_error[0])
self.assertEqual(0, actual_error[1])
self.assertEqual('S321', actual_error[2])
self.assertEqual(' The api_version decorator must be the first '
'decorator on a method.',
actual_error[3])
@mock.patch("pep8._checks",
{'physical_line': {}, 'logical_line': {}, 'tree': {}})
def test_api_version_decorator_good(self):
code = """
class SomeController():
@wsgi.api_version("2.2")
def my_method():
pass
"""
lines = textwrap.dedent(code).strip().splitlines(True)
pep8.register_check(checks.check_api_version_decorator)
checker = pep8.Checker(filename=None, lines=lines)
checker.check_all()
checker.report._deferred_print.sort()
actual_error = checker.report._deferred_print
self.assertEqual(0, len(actual_error))