Skip to content

Commit d8210b9

Browse files
committed
Update logic; create lru log
1 parent 72c57d0 commit d8210b9

File tree

4 files changed

+58
-27
lines changed

4 files changed

+58
-27
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ docs/build/
1212
.mypy_cache/
1313
.incremental_checker_cache.json
1414
.cache
15+
.runtest_log.json
1516

1617
# Packages
1718
*.egg

mypy/waiter.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
This is used for running mypy tests.
44
"""
55

6-
from typing import Dict, List, Optional, Set, Tuple
6+
from typing import Dict, List, Optional, Set, Tuple, Any
77

88
import os
99
from multiprocessing import cpu_count
@@ -110,8 +110,8 @@ class Waiter:
110110
if not waiter.run():
111111
print('error')
112112
"""
113-
114-
TIMING_FILENAME = '.runtest_timing.json'
113+
LOGSIZE = 50
114+
FULL_LOG_FILENAME = '.runtest_log.json'
115115

116116
def __init__(self, limit: int = 0, *, verbosity: int = 0, xfail: List[str] = []) -> None:
117117
self.verbosity = verbosity
@@ -135,18 +135,19 @@ def __init__(self, limit: int = 0, *, verbosity: int = 0, xfail: List[str] = [])
135135
self._note = None # type: Noter
136136
self.times1 = {} # type: Dict[str, float]
137137
self.times2 = {} # type: Dict[str, float]
138+
self.new_log = defaultdict(dict) # type: Dict[str, Dict[str, Any]]
138139

139-
# we likely add only a few tasks, so it's ok to put them in front even if they fast
140-
# much worse if we put them in the back, and one of them turns out to be slow
141-
self.times = defaultdict(lambda: float('inf')) # type: Dict[str, float]
140+
def load_log_file(self) -> Optional[List[Dict[str, Dict[str, Any]]]]:
142141
try:
143-
with open(self.TIMING_FILENAME) as fp:
144-
times = json.load(fp)
145-
self.times.update(times)
146-
self.found_timing_file = True
147-
except Exception:
148-
print('cannot find runtest timing file')
149-
self.found_timing_file = False
142+
# get the last log
143+
with open(self.FULL_LOG_FILENAME) as fp:
144+
test_log = json.load(fp)
145+
except FileNotFoundError:
146+
test_log = []
147+
except json.JSONDecodeError:
148+
print('corrupt test log file {}'.format(self.FULL_LOG_FILENAME))
149+
test_log = []
150+
return test_log
150151

151152
def add(self, cmd: LazySubprocess) -> int:
152153
rv = len(self.queue)
@@ -186,7 +187,8 @@ def _poll_current(self) -> Tuple[int, int]:
186187
code = cmd.process.poll()
187188
if code is not None:
188189
cmd.end_time = time.perf_counter()
189-
self.times[cmd.name] = cmd.end_time - cmd.start_time
190+
self.new_log[cmd.name]['exit_code'] = code
191+
self.new_log[cmd.name]['runtime'] = cmd.end_time - cmd.start_time
190192
return pid, code
191193

192194
def _wait_next(self) -> Tuple[List[str], int, int]:
@@ -260,8 +262,16 @@ def run(self) -> int:
260262
self._note = Noter(len(self.queue))
261263
print('SUMMARY %d tasks selected' % len(self.queue))
262264

263-
self.queue = sorted(self.queue, key=lambda c: self.times[c.name], reverse=True)
264-
print([t.name for t in self.queue][:5])
265+
if self.limit > 1:
266+
logs = self.load_log_file()
267+
if logs:
268+
# we don't know how long a new task takes
269+
# better err by putting it in front in case it is slow:
270+
# a fast task in front hurts performance less than a slow task in the back
271+
default = float('inf')
272+
times = {cmd.name: sum(log[cmd.name].get('runtime', default) for log in logs)
273+
/ len(logs) for cmd in self.queue}
274+
self.queue = sorted(self.queue, key=lambda cmd: times[cmd.name], reverse=True)
265275

266276
sys.stdout.flush()
267277
# Failed tasks.
@@ -296,12 +306,14 @@ def run(self) -> int:
296306
print('*** OK ***')
297307
sys.stdout.flush()
298308

299-
if not self.found_timing_file:
309+
if self.limit > 1:
310+
# log only LOGSIZE most recent tests
311+
test_log = (self.load_log_file() + [self.new_log])[:self.LOGSIZE]
300312
try:
301-
with open(self.TIMING_FILENAME, 'w') as fp:
302-
json.dump(self.times, fp, sort_keys=True, indent=4)
303-
except Exception:
304-
print('cannot save runtest timing file')
313+
with open(self.FULL_LOG_FILENAME, 'w') as fp:
314+
json.dump(test_log, fp, sort_keys=True, indent=4)
315+
except Exception as e:
316+
print('cannot save test log file:', e)
305317

306318
return 0
307319

runtests.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,14 @@ def get_versions(): # type: () -> typing.List[str]
3434
import itertools
3535
import os
3636
import re
37+
import json
3738

3839

3940
# Ideally, all tests would be `discover`able so that they can be driven
4041
# (and parallelized) by an external test driver.
4142

4243
class Driver:
43-
44-
def __init__(self, whitelist: List[str], blacklist: List[str],
44+
def __init__(self, whitelist: List[str], blacklist: List[str], refresh_cache: bool,
4545
arglist: List[str], pyt_arglist: List[str],
4646
verbosity: int, parallel_limit: int,
4747
xfail: List[str], coverage: bool) -> None:
@@ -50,6 +50,9 @@ def __init__(self, whitelist: List[str], blacklist: List[str],
5050
self.arglist = arglist
5151
self.pyt_arglist = pyt_arglist
5252
self.verbosity = verbosity
53+
self.refresh_cache = refresh_cache
54+
if self.refresh_cache:
55+
parallel_limit = 1
5356
self.waiter = Waiter(verbosity=verbosity, limit=parallel_limit, xfail=xfail)
5457
self.sequential = Waiter(verbosity=verbosity, limit=1, xfail=xfail)
5558
self.versions = get_versions()
@@ -58,6 +61,10 @@ def __init__(self, whitelist: List[str], blacklist: List[str],
5861
self.env = dict(os.environ)
5962
self.coverage = coverage
6063

64+
def run(self) -> int:
65+
exit_code = self.sequential.run() & self.waiter.run()
66+
return exit_code
67+
6168
def prepend_path(self, name: str, paths: List[str]) -> None:
6269
old_val = self.env.get(name)
6370
paths = [p for p in paths if isdir(p)]
@@ -300,7 +307,8 @@ def add_samples(driver: Driver) -> None:
300307

301308

302309
def usage(status: int) -> None:
303-
print('Usage: %s [-h | -v | -q | [-x] FILTER | -a ARG | -p ARG] ... [-- FILTER ...]'
310+
print('Usage: %s [-h | -v | -q | --lf | --ff | [-x] FILTER | -a ARG | -p ARG]'
311+
'... [-- FILTER ...]'
304312
% sys.argv[0])
305313
print()
306314
print('Run mypy tests. If given no arguments, run all tests.')
@@ -313,6 +321,12 @@ def usage(status: int) -> None:
313321
print('Options:')
314322
print(' -h, --help show this help')
315323
print(' -v, --verbose increase driver verbosity')
324+
print(' --lf rerun only the tests that failed at the last run '
325+
'(or all if none failed)')
326+
print(' --lf run all tests but run the last failures first. This '
327+
'may re-order tests and thus lead to repeated fixture '
328+
'setup/teardown')
329+
print(' -v, --verbose increase driver verbosity')
316330
print(' -q, --quiet decrease driver verbosity')
317331
print(' -jN run N tasks at once (default: one per CPU)')
318332
print(' -a, --argument ARG pass an argument to myunit tasks')
@@ -322,6 +336,7 @@ def usage(status: int) -> None:
322336
print(' FILTER include tasks matching FILTER')
323337
print(' -x, --exclude FILTER exclude tasks matching FILTER')
324338
print(' -c, --coverage calculate code coverage while running tests')
339+
print(' -r, --refresh-cache refresh timing information')
325340
print(' -- treat all remaining arguments as positional')
326341
sys.exit(status)
327342

@@ -353,6 +368,7 @@ def main() -> None:
353368
arglist = [] # type: List[str]
354369
pyt_arglist = [] # type: List[str]
355370
list_only = False
371+
refresh_cache = False
356372
coverage = False
357373

358374
allow_opts = True
@@ -382,6 +398,8 @@ def main() -> None:
382398
list_only = True
383399
elif a == '-c' or a == '--coverage':
384400
coverage = True
401+
elif a == '-r' or a == '--refresh-cache':
402+
refresh_cache = True
385403
elif a == '-h' or a == '--help':
386404
usage(0)
387405
else:
@@ -399,7 +417,7 @@ def main() -> None:
399417
if not whitelist:
400418
whitelist.append('')
401419

402-
driver = Driver(whitelist=whitelist, blacklist=blacklist,
420+
driver = Driver(whitelist=whitelist, blacklist=blacklist, refresh_cache=refresh_cache,
403421
arglist=arglist, pyt_arglist=pyt_arglist, verbosity=verbosity,
404422
parallel_limit=parallel_limit, xfail=[], coverage=coverage)
405423

@@ -424,7 +442,7 @@ def main() -> None:
424442
driver.list_tasks()
425443
return
426444

427-
exit_code = driver.sequential.run() & driver.waiter.run()
445+
exit_code = driver.run()
428446
t1 = time.perf_counter()
429447
print('total runtime:', t1 - t0, 'sec')
430448

typeshed

Submodule typeshed updated 163 files

0 commit comments

Comments
 (0)