~harlowja/cloud-init/tox-venvs

« back to all changes in this revision

Viewing changes to tools/hacking.py

  • Committer: Joshua Harlow
  • Date: 2014-01-26 01:00:38 UTC
  • Revision ID: harlowja@gmail.com-20140126010038-smsyr4lztcjrpk81
Use a venv for testing and for lint checking

Remove the pep8 & pylint tools and use a venv
with tox instead. Also create a venv that will
run for the given python version to perform the
cloud-init tests.

These help remove the need to install the same
dependencies in the root python environment env
to just run the standard set of tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/env python
2
 
# vim: tabstop=4 shiftwidth=4 softtabstop=4
3
 
 
4
 
# Copyright (c) 2012, Cloudscaling
5
 
# All Rights Reserved.
6
 
#
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
10
 
#
11
 
#         http://www.apache.org/licenses/LICENSE-2.0
12
 
#
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
17
 
#    under the License.
18
 
 
19
 
"""cloudinit HACKING file compliance testing (based off of nova hacking.py)
20
 
 
21
 
built on top of pep8.py
22
 
"""
23
 
 
24
 
import inspect
25
 
import logging
26
 
import re
27
 
import sys
28
 
 
29
 
import pep8
30
 
 
31
 
# Don't need this for testing
32
 
logging.disable('LOG')
33
 
 
34
 
# N1xx comments
35
 
# N2xx except
36
 
# N3xx imports
37
 
# N4xx docstrings
38
 
# N[5-9]XX (future use)
39
 
 
40
 
DOCSTRING_TRIPLE = ['"""', "'''"]
41
 
VERBOSE_MISSING_IMPORT = False
42
 
_missingImport = set([])
43
 
 
44
 
 
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])
55
 
    else:
56
 
        return line
57
 
 
58
 
 
59
 
def cloud_import_alphabetical(physical_line, line_number, lines):
60
 
    """Check for imports in alphabetical order.
61
 
 
62
 
    HACKING guide recommendation for imports:
63
 
    imports in human alphabetical order
64
 
    N306
65
 
    """
66
 
    # handle import x
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"
72
 
    length = [2, 4]
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]))
78
 
 
79
 
 
80
 
def cloud_docstring_start_space(physical_line):
81
 
    """Check for docstring not start with space.
82
 
 
83
 
    HACKING guide recommendation for docstring:
84
 
    Docstring should not start with space
85
 
    N401
86
 
    """
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"
91
 
                " a space")
92
 
 
93
 
 
94
 
def cloud_todo_format(physical_line):
95
 
    """Check for 'TODO()'.
96
 
 
97
 
    HACKING guide recommendation for TODO:
98
 
    Include your name with TODOs as in "#TODO(termie)"
99
 
    N101
100
 
    """
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)"
106
 
 
107
 
 
108
 
def cloud_docstring_one_line(physical_line):
109
 
    """Check one line docstring end.
110
 
 
111
 
    HACKING guide recommendation for one line docstring:
112
 
    A one line docstring looks like this and ends in a period.
113
 
    N402
114
 
    """
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"
120
 
 
121
 
 
122
 
def cloud_docstring_multiline_end(physical_line):
123
 
    """Check multi line docstring end.
124
 
 
125
 
    HACKING guide recommendation for docstring:
126
 
    Docstring should end on a new line
127
 
    N403
128
 
    """
129
 
    pos = max([physical_line.find(i) for i in DOCSTRING_TRIPLE])  # start
130
 
    if (pos != -1 and len(physical_line) == pos):
131
 
        print physical_line
132
 
        if (physical_line[pos + 3] == ' '):
133
 
            return (pos, "N403: multi line docstring end on new line")
134
 
 
135
 
 
136
 
current_file = ""
137
 
 
138
 
 
139
 
def readlines(filename):
140
 
    """Record the current file being tested."""
141
 
    pep8.current_file = filename
142
 
    return open(filename).readlines()
143
 
 
144
 
 
145
 
def add_cloud():
146
 
    """Monkey patch pep8 for cloud-init guidelines.
147
 
 
148
 
    Look for functions that start with cloud_
149
 
    and add them to pep8 module.
150
 
 
151
 
    Assumes you know how to write pep8.py checks
152
 
    """
153
 
    for name, function in globals().items():
154
 
        if not inspect.isfunction(function):
155
 
            continue
156
 
        if name.startswith("cloud_"):
157
 
            exec("pep8.%s = %s" % (name, name))  # pylint: disable=W0122
158
 
 
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}')
162
 
    add_cloud()
163
 
    pep8.current_file = current_file
164
 
    pep8.readlines = readlines
165
 
    try:
166
 
        pep8._main()  # pylint: disable=W0212
167
 
    finally:
168
 
        if len(_missingImport) > 0:
169
 
            print >> sys.stderr, ("%i imports missing in this test environment"
170
 
                    % len(_missingImport))