summaryrefslogtreecommitdiffstats
path: root/tools/hacking.py
blob: 75c795b9ca2631187ac13f99ee4b010e42f210b0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright (c) 2012, Cloudscaling
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""nova HACKING file compliance testing

built on top of pep8.py
"""

import inspect
import os
import re
import sys
import traceback

import pep8

#N1xx comments
#N2xx except
#N3xx imports
#N4xx docstrings
#N5xx dictionaries/lists
#N6xx Calling methods


def nova_todo_format(physical_line):
    """
    nova HACKING guide recommendation for TODO:
    Include your name with TODOs as in "#TODO(termie)"
    N101
    """
    pos = physical_line.find('TODO')
    pos1 = physical_line.find('TODO(')
    pos2 = physical_line.find('#')  # make sure its a comment
    if (pos != pos1 and pos2 >= 0 and pos2 < pos):
        return pos, "NOVA N101: Use TODO(NAME)"


def nova_except_format(logical_line):
    """
    nova HACKING guide recommends not using except:
    Do not write "except:", use "except Exception:" at the very least
    N201
    """
    if logical_line.startswith("except:"):
        return 6, "NOVA N201: no 'except:' at least use 'except Exception:'"


def nova_except_format(logical_line):
    """
    nova HACKING guide recommends not using assertRaises(Exception...):
    Do not use overly broad Exception type
    N202
    """
    if logical_line.startswith("self.assertRaises(Exception"):
        return 1, "NOVA N202: assertRaises Exception too broad"


def nova_one_import_per_line(logical_line):
    """
    nova HACKING guide recommends one import per line:
    Do not import more than one module per line

    Examples:
    BAD: from nova.rpc.common import RemoteError, LOG
    BAD: from sqlalchemy import MetaData, Table
    N301
    """
    pos = logical_line.find(',')
    if (pos > -1 and (logical_line.startswith("import ") or
       (logical_line.startswith("from ") and
       logical_line.split()[2] == "import"))):
        return pos, "NOVA N301: one import per line"


def nova_import_module_only(logical_line):
    """
    nova HACKING guide recommends importing only modules:
    Do not import objects, only modules
    N302 import only modules
    N303 Invalid Import
    N304 Relative Import
    """
    def importModuleCheck(mod, parent=None, added=False):
        """
        If can't find module on first try, recursively check for relative
        imports
        """
        current_path = os.path.dirname(pep8.current_file)
        try:
            valid = True
            if parent:
                parent_mod = __import__(parent, globals(), locals(), [mod], -1)
                valid = inspect.ismodule(getattr(parent_mod, mod))
            else:
                __import__(mod, globals(), locals(), [], -1)
                valid = inspect.ismodule(sys.modules[mod])
            if not valid:
                if added:
                    sys.path.pop()
                    added = False
                    return logical_line.find(mod), ("NOVA N304: No relative "
                        "imports. '%s' is a relative import" % logical_line)

                return logical_line.find(mod), ("NOVA N302: import only "
                    "modules. '%s' does not import a module" % logical_line)

        except (ImportError, NameError):
            if not added:
                added = True
                sys.path.append(current_path)
                return importModuleCheck(mod, parent, added)
            else:
                print >> sys.stderr, ("ERROR: import '%s' failed, couldn't "
                    "find module" % logical_line)
                added = False
                sys.path.pop()
                return

        except AttributeError:
            # Invalid import
            return logical_line.find(mod), ("NOVA N303: Invalid import, "
                "AttributeError raised")

    split_line = logical_line.split()

    # handle "import x"
    # handle "import x as y"
    if (logical_line.startswith("import ") and "," not in logical_line and
            (len(split_line) == 2 or
            (len(split_line) == 4 and split_line[2] == "as"))):
        mod = split_line[1]
        return importModuleCheck(mod)

    # handle "from x import y"
    # handle "from x import y as z"
    elif (logical_line.startswith("from ") and "," not in logical_line and
           split_line[2] == "import" and split_line[3] != "*" and
           (len(split_line) == 4 or
           (len(split_line) == 6  and split_line[4] == "as"))):
        mod = split_line[3]
        return importModuleCheck(mod, split_line[1])

    # TODO(jogo) handle "from x import *"

#TODO(jogo) Dict and list objects

current_file = ""


def readlines(filename):
    """
    record the current file being tested
    """
    pep8.current_file = filename
    return open(filename).readlines()


def add_nova():
    """
    Look for functions that start with nova_  and have arguments
    and add them to pep8 module
    Assumes you know how to write pep8.py checks
    """
    for name, function in globals().items():
        if not inspect.isfunction(function):
            continue
        args = inspect.getargspec(function)[0]
        if args and name.startswith("nova"):
            exec("pep8.%s = %s" % (name, name))

if __name__ == "__main__":
    #include nova path
    sys.path.append(os.getcwd())
    #NOVA error codes start with an N
    pep8.ERRORCODE_REGEX = re.compile(r'[EWN]\d{3}')
    add_nova()
    pep8.current_file = current_file
    pep8.readlines = readlines
    pep8._main()