forked from exercism/python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtemplate_status.py
executable file
·136 lines (113 loc) · 4.29 KB
/
template_status.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
#!/usr/bin/env python3.7
import argparse
from argparse import Namespace
from enum import IntEnum, auto
from fnmatch import fnmatch
import logging
from pathlib import Path
import shlex
from subprocess import check_call, DEVNULL, CalledProcessError
import sys
from typing import List, Iterator
from data import Config, ExerciseInfo, ExerciseStatus
from generate_tests import clone_if_missing
from githelp import Repo
from test_exercises import check_assignment
DEFAULT_SPEC_LOCATION = Path('.problem-specifications')
logging.basicConfig(format="%(levelname)s:%(message)s")
logger = logging.getLogger("generator")
logger.setLevel(logging.WARN)
class TemplateStatus(IntEnum):
OK = auto()
MISSING = auto()
INVALID = auto()
TEST_FAILURE = auto()
def exec_cmd(cmd: str) -> bool:
try:
args = shlex.split(cmd)
if logger.isEnabledFor(logging.DEBUG):
check_call(args)
else:
check_call(args, stderr=DEVNULL, stdout=DEVNULL)
return True
except CalledProcessError as e:
logger.debug(str(e))
return False
def generate_template(exercise: ExerciseInfo, spec_path: Path) -> bool:
script = Path('bin/generate_tests.py')
return exec_cmd(f'{script} --verbose --spec-path "{spec_path}" {exercise.slug}')
def run_tests(exercise: ExerciseInfo) -> bool:
return check_assignment(exercise, quiet=True) == 0
def get_status(exercise: ExerciseInfo, spec_path: Path) -> TemplateStatus:
if exercise.template_path.is_file():
if generate_template(exercise, spec_path):
if run_tests(exercise):
logging.info(f"{exercise.slug}: OK")
return TemplateStatus.OK
else:
return TemplateStatus.TEST_FAILURE
else:
return TemplateStatus.INVALID
else:
return TemplateStatus.MISSING
def set_loglevel(opts: Namespace):
if opts.quiet:
logger.setLevel(logging.FATAL)
elif opts.verbose >= 2:
logger.setLevel(logging.DEBUG)
elif opts.verbose >= 1:
logger.setLevel(logging.INFO)
def filter_exercises(exercises: List[ExerciseInfo], pattern: str) -> Iterator[ExerciseInfo]:
for exercise in exercises:
if exercise.status != ExerciseStatus.Deprecated:
if exercise.type == 'concept':
# Concept exercises are not generated
continue
if fnmatch(exercise["slug"], pattern):
yield exercise
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("exercise_pattern", nargs="?", default="*", metavar="EXERCISE")
parser.add_argument("-v", "--verbose", action="count", default=0)
parser.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("--stop-on-failure", action="store_true")
parser.add_argument(
"-p",
"--spec-path",
default=DEFAULT_SPEC_LOCATION,
type=Path,
help=(
"path to clone of exercism/problem-specifications " "(default: %(default)s)"
),
)
opts = parser.parse_args()
set_loglevel(opts)
if not opts.spec_path.is_dir():
logger.error(f"{opts.spec_path} is not a directory")
sys.exit(1)
with clone_if_missing(repo=Repo.ProblemSpecifications, directory=opts.spec_path):
result = True
buckets = {
TemplateStatus.MISSING: [],
TemplateStatus.INVALID: [],
TemplateStatus.TEST_FAILURE: [],
}
config = Config.load()
for exercise in filter_exercises(config.exercises.all()):
status = get_status(exercise, opts.spec_path)
if status == TemplateStatus.OK:
logger.info(f"{exercise.slug}: {status.name}")
else:
buckets[status].append(exercise.slug)
result = False
if opts.stop_on_failure:
logger.error(f"{exercise.slug}: {status.name}")
break
if not opts.quiet and not opts.stop_on_failure:
for status, bucket in sorted(buckets.items()):
if bucket:
print(f"The following exercises have status '{status.name}'")
for exercise in sorted(bucket):
print(f' {exercise}')
if not result:
sys.exit(1)