2
# vim: tabstop=4 shiftwidth=4 softtabstop=4
4
# Copyright (c) 2012, Cloudscaling
7
# Licensed under the Apache License, Version 2.0 (the "License"); you may
8
# not use this file except in compliance with the License. You may obtain
9
# a copy of the License at
11
# http://www.apache.org/licenses/LICENSE-2.0
13
# Unless required by applicable law or agreed to in writing, software
14
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16
# License for the specific language governing permissions and limitations
19
"""cloudinit HACKING file compliance testing (based off of nova hacking.py)
21
built on top of pep8.py
31
# Don't need this for testing
32
logging.disable('LOG')
38
# N[5-9]XX (future use)
40
DOCSTRING_TRIPLE = ['"""', "'''"]
41
VERBOSE_MISSING_IMPORT = False
42
_missingImport = set([])
45
def import_normalize(line):
46
# convert "from x import y" to "import x.y"
47
# handle "from x import y as z" to "import x.y as z"
48
split_line = line.split()
49
if (line.startswith("from ") and "," not in line and
50
split_line[2] == "import" and split_line[3] != "*" and
51
split_line[1] != "__future__" and
52
(len(split_line) == 4 or
53
(len(split_line) == 6 and split_line[4] == "as"))):
54
return "import %s.%s" % (split_line[1], split_line[3])
59
def cloud_import_alphabetical(physical_line, line_number, lines):
60
"""Check for imports in alphabetical order.
62
HACKING guide recommendation for imports:
63
imports in human alphabetical order
67
# use .lower since capitalization shouldn't dictate order
68
split_line = import_normalize(physical_line.strip()).lower().split()
69
split_previous = import_normalize(lines[line_number - 2])
70
split_previous = split_previous.strip().lower().split()
71
# with or without "as y"
73
if (len(split_line) in length and len(split_previous) in length and
74
split_line[0] == "import" and split_previous[0] == "import"):
75
if split_line[1] < split_previous[1]:
76
return (0, "N306: imports not in alphabetical order (%s, %s)"
77
% (split_previous[1], split_line[1]))
80
def cloud_docstring_start_space(physical_line):
81
"""Check for docstring not start with space.
83
HACKING guide recommendation for docstring:
84
Docstring should not start with space
87
pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE]) # start
88
if (pos != -1 and len(physical_line) > pos + 1):
89
if (physical_line[pos + 3] == ' '):
90
return (pos, "N401: one line docstring should not start with"
94
def cloud_todo_format(physical_line):
95
"""Check for 'TODO()'.
97
HACKING guide recommendation for TODO:
98
Include your name with TODOs as in "#TODO(termie)"
101
pos = physical_line.find('TODO')
102
pos1 = physical_line.find('TODO(')
103
pos2 = physical_line.find('#') # make sure it's a comment
104
if (pos != pos1 and pos2 >= 0 and pos2 < pos):
105
return pos, "N101: Use TODO(NAME)"
108
def cloud_docstring_one_line(physical_line):
109
"""Check one line docstring end.
111
HACKING guide recommendation for one line docstring:
112
A one line docstring looks like this and ends in a period.
115
pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE]) # start
116
end = max([physical_line[-4:-1] == i for i in DOCSTRING_TRIPLE]) # end
117
if (pos != -1 and end and len(physical_line) > pos + 4):
118
if (physical_line[-5] != '.'):
119
return pos, "N402: one line docstring needs a period"
122
def cloud_docstring_multiline_end(physical_line):
123
"""Check multi line docstring end.
125
HACKING guide recommendation for docstring:
126
Docstring should end on a new line
129
pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE]) # start
130
if (pos != -1 and len(physical_line) == pos):
132
if (physical_line[pos + 3] == ' '):
133
return (pos, "N403: multi line docstring end on new line")
139
def readlines(filename):
140
"""Record the current file being tested."""
141
pep8.current_file = filename
142
return open(filename).readlines()
146
"""Monkey patch pep8 for cloud-init guidelines.
148
Look for functions that start with cloud_
149
and add them to pep8 module.
151
Assumes you know how to write pep8.py checks
153
for name, function in globals().items():
154
if not inspect.isfunction(function):
156
if name.startswith("cloud_"):
157
exec("pep8.%s = %s" % (name, name)) # pylint: disable=W0122
159
if __name__ == "__main__":
160
# NOVA based 'hacking.py' error codes start with an N
161
pep8.ERRORCODE_REGEX = re.compile(r'[EWN]\d{3}')
163
pep8.current_file = current_file
164
pep8.readlines = readlines
166
pep8._main() # pylint: disable=W0212
168
if len(_missingImport) > 0:
169
print >> sys.stderr, ("%i imports missing in this test environment"
170
% len(_missingImport))