|
1 | 1 | #!/usr/bin/env python3
|
2 |
| -"""Mypy test runner.""" |
| 2 | +from sys import exit |
3 | 3 |
|
4 |
| -from typing import List, Optional, Set, Iterable, Tuple |
5 |
| - |
6 |
| -import itertools |
7 |
| -import os |
8 |
| -from os.path import join, isdir |
9 |
| -import sys |
10 |
| - |
11 |
| -from waiter import Waiter, LazySubprocess |
12 |
| -from mypy.test.testsamples import find_files, file_to_module |
13 |
| - |
14 |
| - |
15 |
| -def get_versions() -> List[str]: |
16 |
| - # generates list of python versions to use. |
17 |
| - # For Python2, this is only [2.7]. |
18 |
| - # Otherwise, it is [3.x, ..., 3.1, 3.0], where x is the version |
19 |
| - # of the running interpreter. |
20 |
| - major = sys.version_info[0] |
21 |
| - minor = sys.version_info[1] |
22 |
| - if major == 2: |
23 |
| - return ['2.7'] |
24 |
| - else: |
25 |
| - return ['%d.%d' % (major, i) for i in range(minor, -1, -1)] |
26 |
| - |
27 |
| - |
28 |
| -class Driver: |
29 |
| - |
30 |
| - def __init__(self, *, whitelist: List[str], blacklist: List[str], |
31 |
| - lf: bool, ff: bool, |
32 |
| - arglist: List[str], pyt_arglist: List[str], |
33 |
| - verbosity: int, parallel_limit: int, |
34 |
| - xfail: List[str], coverage: bool) -> None: |
35 |
| - self.whitelist = whitelist |
36 |
| - self.blacklist = blacklist |
37 |
| - self.arglist = arglist |
38 |
| - self.pyt_arglist = pyt_arglist |
39 |
| - self.verbosity = verbosity |
40 |
| - self.waiter = Waiter(verbosity=verbosity, limit=parallel_limit, xfail=xfail, lf=lf, ff=ff) |
41 |
| - self.versions = get_versions() |
42 |
| - self.cwd = os.getcwd() |
43 |
| - self.env = dict(os.environ) |
44 |
| - self.coverage = coverage |
45 |
| - |
46 |
| - def prepend_path(self, name: str, paths: List[str]) -> None: |
47 |
| - old_val = self.env.get(name) |
48 |
| - paths = [p for p in paths if isdir(p)] |
49 |
| - if not paths: |
50 |
| - return |
51 |
| - if old_val is not None: |
52 |
| - new_val = os.pathsep.join(itertools.chain(paths, [old_val])) |
53 |
| - else: |
54 |
| - new_val = os.pathsep.join(paths) |
55 |
| - self.env[name] = new_val |
56 |
| - |
57 |
| - def allow(self, name: str) -> bool: |
58 |
| - if any(f in name for f in self.whitelist): |
59 |
| - if not any(f in name for f in self.blacklist): |
60 |
| - if self.verbosity >= 2: |
61 |
| - print('SELECT #%d %s' % (len(self.waiter.queue), name)) |
62 |
| - return True |
63 |
| - if self.verbosity >= 3: |
64 |
| - print('OMIT %s' % name) |
65 |
| - return False |
66 |
| - |
67 |
| - def add_mypy_cmd(self, name: str, mypy_args: List[str], cwd: Optional[str] = None) -> None: |
68 |
| - full_name = 'check %s' % name |
69 |
| - if not self.allow(full_name): |
70 |
| - return |
71 |
| - args = [sys.executable, '-m', 'mypy'] + mypy_args |
72 |
| - args.append('--show-traceback') |
73 |
| - args.append('--no-site-packages') |
74 |
| - self.waiter.add(LazySubprocess(full_name, args, cwd=cwd, env=self.env)) |
75 |
| - |
76 |
| - def add_mypy_modules(self, name: str, modules: Iterable[str], cwd: Optional[str] = None, |
77 |
| - extra_args: Optional[List[str]] = None) -> None: |
78 |
| - args = extra_args or [] |
79 |
| - args.extend(list(itertools.chain(*(['-m', mod] for mod in modules)))) |
80 |
| - self.add_mypy_cmd(name, args, cwd=cwd) |
81 |
| - |
82 |
| - def add_pytest(self, files: List[Tuple[str, str]], coverage: bool = True) -> None: |
83 |
| - pytest_files = [name for kind, name in files |
84 |
| - if self.allow('pytest {} {}'.format(kind, name))] |
85 |
| - if not pytest_files: |
86 |
| - return |
87 |
| - pytest_args = pytest_files + self.arglist + self.pyt_arglist |
88 |
| - if coverage and self.coverage: |
89 |
| - args = [sys.executable, '-m', 'pytest', '--cov=mypy'] + pytest_args |
90 |
| - else: |
91 |
| - args = [sys.executable, '-m', 'pytest'] + pytest_args |
92 |
| - |
93 |
| - self.waiter.add(LazySubprocess('pytest', args, env=self.env, |
94 |
| - passthrough=self.verbosity), |
95 |
| - sequential=True) |
96 |
| - |
97 |
| - def add_flake8(self, cwd: Optional[str] = None) -> None: |
98 |
| - name = 'lint' |
99 |
| - if not self.allow(name): |
100 |
| - return |
101 |
| - largs = ['flake8', '-j0'] |
102 |
| - env = self.env |
103 |
| - self.waiter.add(LazySubprocess(name, largs, cwd=cwd, env=env)) |
104 |
| - |
105 |
| - def list_tasks(self) -> None: |
106 |
| - for id, task in enumerate(self.waiter.queue): |
107 |
| - print('{id}:{task}'.format(id=id, task=task.name)) |
108 |
| - |
109 |
| - |
110 |
| -def test_path(*names: str) -> List[str]: |
111 |
| - return [os.path.join('mypy', 'test', '{}.py'.format(name)) |
112 |
| - for name in names] |
113 |
| - |
114 |
| - |
115 |
| -PYTEST_FILES = test_path( |
116 |
| - 'testcheck', |
117 |
| - 'testextensions', |
118 |
| - 'testdeps', |
119 |
| - 'testdiff', |
120 |
| - 'testfinegrained', |
121 |
| - 'testfinegrainedcache', |
122 |
| - 'testmerge', |
123 |
| - 'testtransform', |
124 |
| - 'testtypegen', |
125 |
| - 'testparse', |
126 |
| - 'testsemanal', |
127 |
| - 'testerrorstream', |
128 |
| - # non-data-driven: |
129 |
| - 'testgraph', |
130 |
| - 'testinfer', |
131 |
| - 'testmoduleinfo', |
132 |
| - 'teststubgen', |
133 |
| - 'testargs', |
134 |
| - 'testreports', |
135 |
| - 'testsolve', |
136 |
| - 'testsubtypes', |
137 |
| - 'testtypes', |
138 |
| -) |
139 |
| - |
140 |
| -SLOW_FILES = test_path( |
141 |
| - 'testpep561', |
142 |
| - 'testpythoneval', |
143 |
| - 'testcmdline', |
144 |
| - 'teststubgen', |
145 |
| - 'testsamples', |
146 |
| -) |
147 |
| - |
148 |
| -SELFCHECK_FILES = test_path( |
149 |
| - 'testselfcheck', |
150 |
| -) |
151 |
| - |
152 |
| - |
153 |
| -def add_pytest(driver: Driver) -> None: |
154 |
| - for f in find_files('mypy', prefix='test', suffix='.py'): |
155 |
| - assert f in PYTEST_FILES + SLOW_FILES + SELFCHECK_FILES, f |
156 |
| - driver.add_pytest([('unit-test', name) for name in PYTEST_FILES] + |
157 |
| - [('integration', name) for name in SLOW_FILES] + |
158 |
| - [('self-check', name) for name in SELFCHECK_FILES]) |
159 |
| - |
160 |
| - |
161 |
| -def usage(status: int) -> None: |
162 |
| - print('Usage: %s [-h | -v | -q | --lf | --ff | [-x] FILTER | -a ARG | -p ARG]' |
163 |
| - '... [-- FILTER ...]' |
164 |
| - % sys.argv[0]) |
165 |
| - print() |
166 |
| - print('Run mypy tests. If given no arguments, run all tests.') |
167 |
| - print() |
168 |
| - print('Examples:') |
169 |
| - print(' %s unit-test (run unit tests only)' % sys.argv[0]) |
170 |
| - print(' %s testcheck (run type checking unit tests only)' % sys.argv[0]) |
171 |
| - print(' %s "pytest unit-test" -a -k -a Tuple' % sys.argv[0]) |
172 |
| - print(' (run all pytest unit tests with "Tuple" in test name)') |
173 |
| - print() |
174 |
| - print('You can also run pytest directly without using %s:' % sys.argv[0]) |
175 |
| - print(' pytest mypy/test/testcheck.py -k Tuple') |
176 |
| - print() |
177 |
| - print('Options:') |
178 |
| - print(' -h, --help show this help') |
179 |
| - print(' -v, --verbose increase driver verbosity') |
180 |
| - print(' --lf rerun only the tests that failed at the last run') |
181 |
| - print(' --ff run all tests but run the last failures first') |
182 |
| - print(' -q, --quiet decrease driver verbosity') |
183 |
| - print(' -jN run N tasks at once (default: one per CPU)') |
184 |
| - print(' -p, --pytest_arg ARG pass an argument to pytest tasks') |
185 |
| - print(' (-v: verbose; glob pattern: filter by test name)') |
186 |
| - print(' -l, --list list included tasks (after filtering) and exit') |
187 |
| - print(' FILTER include tasks matching FILTER') |
188 |
| - print(' -x, --exclude FILTER exclude tasks matching FILTER') |
189 |
| - print(' -c, --coverage calculate code coverage while running tests') |
190 |
| - print(' -- treat all remaining arguments as positional') |
191 |
| - sys.exit(status) |
192 |
| - |
193 |
| - |
194 |
| -def sanity() -> None: |
195 |
| - paths = os.getenv('PYTHONPATH') |
196 |
| - if paths is None: |
197 |
| - return |
198 |
| - failed = False |
199 |
| - for p in paths.split(os.pathsep): |
200 |
| - if not os.path.isabs(p): |
201 |
| - print('Relative PYTHONPATH entry %r' % p) |
202 |
| - failed = True |
203 |
| - if failed: |
204 |
| - print('Please use absolute so that chdir() tests can work.') |
205 |
| - print('Cowardly refusing to continue.') |
206 |
| - sys.exit(1) |
207 |
| - |
208 |
| - |
209 |
| -def main() -> None: |
210 |
| - import time |
211 |
| - t0 = time.perf_counter() |
212 |
| - sanity() |
213 |
| - |
214 |
| - verbosity = 0 |
215 |
| - parallel_limit = 0 |
216 |
| - whitelist = [] # type: List[str] |
217 |
| - blacklist = [] # type: List[str] |
218 |
| - arglist = [] # type: List[str] |
219 |
| - pyt_arglist = [] # type: List[str] |
220 |
| - lf = False |
221 |
| - ff = False |
222 |
| - list_only = False |
223 |
| - coverage = False |
224 |
| - |
225 |
| - allow_opts = True |
226 |
| - curlist = whitelist |
227 |
| - for a in sys.argv[1:]: |
228 |
| - if not (curlist is arglist or curlist is pyt_arglist) and allow_opts and a.startswith('-'): |
229 |
| - if curlist is not whitelist: |
230 |
| - break |
231 |
| - if a == '--': |
232 |
| - allow_opts = False |
233 |
| - elif a == '-v' or a == '--verbose': |
234 |
| - verbosity += 1 |
235 |
| - elif a == '-q' or a == '--quiet': |
236 |
| - verbosity -= 1 |
237 |
| - elif a.startswith('-j'): |
238 |
| - try: |
239 |
| - parallel_limit = int(a[2:]) |
240 |
| - except ValueError: |
241 |
| - usage(1) |
242 |
| - elif a == '-x' or a == '--exclude': |
243 |
| - curlist = blacklist |
244 |
| - elif a == '-a' or a == '--argument': |
245 |
| - curlist = arglist |
246 |
| - elif a == '-p' or a == '--pytest_arg': |
247 |
| - curlist = pyt_arglist |
248 |
| - # will also pass this option to pytest |
249 |
| - elif a == '--lf': |
250 |
| - lf = True |
251 |
| - # will also pass this option to pytest |
252 |
| - elif a == '--ff': |
253 |
| - ff = True |
254 |
| - elif a == '-l' or a == '--list': |
255 |
| - list_only = True |
256 |
| - elif a == '-c' or a == '--coverage': |
257 |
| - coverage = True |
258 |
| - elif a == '-h' or a == '--help': |
259 |
| - usage(0) |
260 |
| - else: |
261 |
| - usage(1) |
262 |
| - else: |
263 |
| - curlist.append(a) |
264 |
| - curlist = whitelist |
265 |
| - if curlist is blacklist: |
266 |
| - sys.exit('-x must be followed by a filter') |
267 |
| - if curlist is arglist: |
268 |
| - sys.exit('-a must be followed by an argument') |
269 |
| - if curlist is pyt_arglist: |
270 |
| - sys.exit('-p must be followed by an argument') |
271 |
| - if lf and ff: |
272 |
| - sys.exit('use either --lf or --ff, not both') |
273 |
| - # empty string is a substring of all names |
274 |
| - if not whitelist: |
275 |
| - whitelist.append('') |
276 |
| - if lf: |
277 |
| - pyt_arglist.append('--lf') |
278 |
| - if ff: |
279 |
| - pyt_arglist.append('--ff') |
280 |
| - if verbosity >= 1: |
281 |
| - pyt_arglist.extend(['-v'] * verbosity) |
282 |
| - elif verbosity < 0: |
283 |
| - pyt_arglist.extend(['-q'] * (-verbosity)) |
284 |
| - if parallel_limit: |
285 |
| - if '-n' not in pyt_arglist: |
286 |
| - pyt_arglist.append('-n{}'.format(parallel_limit)) |
287 |
| - |
288 |
| - driver = Driver(whitelist=whitelist, blacklist=blacklist, lf=lf, ff=ff, |
289 |
| - arglist=arglist, pyt_arglist=pyt_arglist, verbosity=verbosity, |
290 |
| - parallel_limit=parallel_limit, xfail=[], coverage=coverage) |
291 |
| - |
292 |
| - driver.prepend_path('PATH', [join(driver.cwd, 'scripts')]) |
293 |
| - driver.prepend_path('MYPYPATH', [driver.cwd]) |
294 |
| - driver.prepend_path('PYTHONPATH', [driver.cwd]) |
295 |
| - |
296 |
| - driver.add_flake8() |
297 |
| - add_pytest(driver) |
298 |
| - |
299 |
| - if list_only: |
300 |
| - driver.list_tasks() |
301 |
| - return |
302 |
| - |
303 |
| - exit_code = driver.waiter.run() |
304 |
| - t1 = time.perf_counter() |
305 |
| - print('total runtime:', t1 - t0, 'sec') |
306 |
| - |
307 |
| - if verbosity >= 1: |
308 |
| - times = driver.waiter.times2 if verbosity >= 2 else driver.waiter.times1 |
309 |
| - times_sortable = ((t, tp) for (tp, t) in times.items()) |
310 |
| - for total_time, test_type in sorted(times_sortable, reverse=True): |
311 |
| - print('total time in %s: %f' % (test_type, total_time)) |
312 |
| - |
313 |
| - sys.exit(exit_code) |
314 |
| - |
315 |
| - |
316 |
| -if __name__ == '__main__': |
317 |
| - main() |
| 4 | +print('runtests.py is no more. Use pytest && python3 -m mypy -p mypy') |
| 5 | +exit(1) |
0 commit comments