-
Notifications
You must be signed in to change notification settings - Fork 2
/
helpers.py
216 lines (141 loc) · 5.39 KB
/
helpers.py
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
"""Callables that accept a path to a file and check it for various things."""
import subprocess
from six import iteritems
from testtube.conf import Settings
from testtube.renderer import Renderer
class HardTestFailure(Exception):
"""Test failure that should abort test processing."""
pass
class Helper(object):
"""Generic helper class for writing callable testtube tests.
When a test that extends Helper is instantiated and called, it will:
1. Set self.changed (path to changed file) and self.match
(regex match object) to the passed in values.
2. Call self.setup()
3. Set result = self.test()
4. If the test passed, it will call self.success(result)
5. If the test failed, it will call self.failure(result)
6. Call self.tear_down(result)
7. Return the result
self.test(), called in step 3, invokves the class attribute `command` via
subporcess.call() and passes that command the arguments returned by
get_args().
Any method in the execution squence is overridable to enable helpers to be
customized as necessary. See Helper subclasses for examples.
"""
command = ''
renderer = Renderer()
def __init__(self, **kwargs):
"""Configure and return callable test.
All keword arguments except `changed` and `match` are set as object
attributes.
"""
self.all_files = False
self.fail_fast = False
self.bells = 3
self.name = self.__class__.__name__
# Override default settings with passed in values.
for setting, value in iteritems(kwargs):
setattr(self, setting, value)
# These properites are provided by __call__ and are not configurable.
self.changed = ''
self.match = ''
def setup(self):
"""Prepare the helper class to execute the test."""
changed = "source directory" if self.all_files else self.changed
self.renderer.notice(
'Executing %s against %s.\n' % (self.name, changed))
def tear_down(self, result):
"""Clean up test execution."""
self.renderer.notice()
def success(self, result):
"""Handle test success."""
self.renderer.success('Test passed.')
def failure(self, result):
"""Hanlde test failure."""
self.renderer.audible_alert(self.bells)
self.renderer.failure('Test failed.')
if self.fail_fast:
raise HardTestFailure('Fail fast is enabled, aborting test run.')
def test(self):
"""Execute the configured command with appropriate arguments."""
if not self.command:
return True
return subprocess.call([self.command] + self.get_args()) == 0
def get_args(self):
"""Generate argumnets for the test process."""
if self.all_files:
return [Settings.SRC_DIR]
return [self.changed]
def __call__(self, changed, match):
"""Test a changed file with the configured command."""
self.changed = changed
self.match = match
self.setup()
result = self.test()
if result:
self.success(result)
if not result:
self.failure(result)
self.tear_down(result)
return result
class Pep8(Helper):
"""Execute PEP8 against a file or configured project directory."""
command = 'pep8'
class Pyflakes(Helper):
"""Execute pyflakes against a file or configured project directory."""
command = 'pyflakes'
class Frosted(Helper):
"""Execute pyflakes against a file or configured project directory."""
command = 'frosted'
def get_args(self):
"""Generate frosted arguments."""
if self.all_files:
return ['-r', Settings.SRC_DIR]
return [self.changed]
class Nosetests(Helper):
"""Execute nosetests in the configured project directory.
Note that this helper cannot be configured to run against only the
changed file.
"""
command = 'nosetests'
def __init__(self, **kwargs):
"""Generate a `nosetests` callable.
all_files=False will be ignored. This helper can only operate on
all files.
"""
kwargs['all_files'] = True
super(Nosetests, self).__init__(**kwargs)
def get_args(self, *args, **kwargs):
"""Return empty list of arguments.
Nose can be configured via a setup.cfg file in settings.SRC_DIR
"""
return []
class Flake8(Helper):
"""Execute flake8 against a file or configured project directory."""
command = 'flake8'
class Pep257(Helper):
"""Execute pep257 against a file or configured project directory."""
command = 'pep257'
class PythonSetupPyTest(Helper):
"""Execute `python setup.py test`."""
command = 'python'
def get_args(self):
"""Return list of arguments for `python`."""
return ['setup.py', 'test']
class ClearScreen(Helper):
"""Clear the contents of the terminal window."""
command = 'clear'
def __init__(self, **kwargs):
"""Generate a `ClearScreen` callable.
all_files=False will be ignored because this helper doesn't operate
on specific files.
"""
kwargs['all_files'] = True
super(ClearScreen, self).__init__(**kwargs)
def success(self, *args):
"""Output nothing on successs."""
pass
def setup(self):
"""Output nothing on test setup."""
pass