-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathautograde.py
More file actions
161 lines (138 loc) · 5.03 KB
/
autograde.py
File metadata and controls
161 lines (138 loc) · 5.03 KB
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
import os, pathlib, subprocess, time
CURRENT_DIRECTORY = 0
COMPILE_MESSAGE_LINE_LIMIT = 10
def in_each_repo(function, root_path = CURRENT_DIRECTORY):
if root_path == CURRENT_DIRECTORY:
root_path = pathlib.PosixPath()
for child in root_path.iterdir():
# skip __pycache__
if child.is_dir() and child != '__pycache__':
in_subdir(lambda path: assess_one_repo(function), child)
print_banner()
def in_subdir(function, child_name):
current_dir = current_path()
subdir = current_dir / child_name
try:
os.chdir(subdir)
result = function(subdir)
os.chdir(current_dir)
return result
except FileNotFoundError:
print('ERROR no such directory "' + child_name + '"')
return None
def assess_one_repo(function):
print_banner()
parent = current_path()
repo_dir_name = parent.parts[-1]
print(str(repo_dir_name))
return function()
def current_path():
return pathlib.PosixPath(os.getcwd())
def print_banner():
print('=' * 79)
def print_repo_name(path):
print(path.parts[-1])
# return True on success, False on failure
def print_line_number(path, line_number_from_1):
ok = True
print(' line ' + str(line_number_from_1) + ' of ' + str(path) + ':')
try:
with open(path) as f:
lines = f.readlines()
index = line_number_from_1 - 1
if (index < 0) or (index >= len(lines)):
print('(that line does not exist)')
ok = False
else:
print(lines[index])
except OSError:
print('(file does not exist)')
ok = False
return ok
# return True if there was at least one match, False otherwise
def print_matching_lines(path, substring):
found_match = False
try:
with open(path) as f:
lines = f.readlines()
for i in range(len(lines)):
line = lines[i]
if substring in line:
print('line ' + str(i+1) + ' of ' + str(path) + ': ' + line)
found_match = True
except OSError:
print('(' + str(path) + ' does not exist)')
return found_match
def print_file(path):
try:
with open(path) as f:
print(f.read())
except OSError:
print('(' + str(path) + ' does not exist)')
def compiles_cleanly_in_subdir(subdir, source_filename):
in_subdir(lambda path: compiles_cleanly(source_filename), subdir)
def compiles_cleanly(source_filename):
result = True
print(str(source_filename) + ' compiles cleanly:', end='')
messages = command(['g++', source_filename])
if not messages:
print('YES')
else:
print('NO, messages:')
for line in messages.splitlines()[:COMPILE_MESSAGE_LINE_LIMIT]:
print(line)
result = False
print('')
return result
# return a tuple (output, exit_code)
def output_and_exit_code(args, stdin_string=None, timeout=None):
try:
proc = subprocess.run(args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
input=stdin_string,
timeout=timeout)
return (proc.stdout, proc.returncode)
except subprocess.TimeoutExpired:
print("TIMEOUT AFTER " + str(timeout) + " seconds")
return ('', 255)
# If the command succeeds (exit code 0), return None.
# Otherwise return a string with stdout and stderr, probably containing error messages.
def command(args):
messages, exit_code = output_and_exit_code(args)
if exit_code == 0:
return None
else:
return messages
def print_output_and_exit_code_in_subdir(subdir, source_filename):
print('running ' + source_filename + '...')
def subdir_stuff(path):
print(' ', end='')
if compiles_cleanly(source_filename):
output, exit_code = output_and_exit_code(['./a.out'])
print(' EXIT CODE = ' + str(exit_code), 'OUTPUT ON NEXT LINE:')
print(output + '(end of output)\n')
in_subdir(subdir_stuff, subdir)
def commandline_test_case_in_subdir(subdir, source_filename, arguments, stdin_string=None, timeout=None):
print('running ' + source_filename + '...')
def subdir_stuff(path):
print(' ', end='')
if compiles_cleanly(source_filename):
command = ['./a.out'] + arguments
print('$ ./a.out ' + ' '.join(arguments))
output, exit_code = output_and_exit_code(command, stdin_string, timeout)
print('-OUTPUT BEGINS-')
print(output)
print('-OUTPUT ENDS-')
in_subdir(subdir_stuff, subdir)
def git_log():
output, exit_code = output_and_exit_code(['git', 'log'])
print('git log:\n' + output)
# default seconds is slightly larger than 1 so that C++ srand(time(0)) will
# have a different seed.
def repeat_with_delay(function, count, seconds=1.01):
for i in range(count):
if i > 0:
time.sleep(seconds)
function()