Skip to content

Commit 1231834

Browse files
committed
commit inicial
1 parent fd46bb0 commit 1231834

File tree

1 file changed

+351
-0
lines changed

1 file changed

+351
-0
lines changed

autograder.py

+351
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
# autograder.py
2+
# -------------
3+
# Licensing Information: You are free to use or extend these projects for
4+
# educational purposes provided that (1) you do not distribute or publish
5+
# solutions, (2) you retain this notice, and (3) you provide clear
6+
# attribution to UC Berkeley, including a link to http://ai.berkeley.edu.
7+
#
8+
# Attribution Information: The Pacman AI projects were developed at UC Berkeley.
9+
# The core projects and autograders were primarily created by John DeNero
10+
11+
# Student side autograding was added by Brad Miller, Nick Hay, and
12+
# Pieter Abbeel ([email protected]).
13+
14+
15+
# imports from python standard library
16+
import grading
17+
import imp
18+
import optparse
19+
import os
20+
import re
21+
import sys
22+
import projectParams
23+
import random
24+
random.seed(0)
25+
try:
26+
from pacman import GameState
27+
except:
28+
pass
29+
30+
# register arguments and set default values
31+
def readCommand(argv):
32+
parser = optparse.OptionParser(description = 'Run public tests on student code')
33+
parser.set_defaults(generateSolutions=False, edxOutput=False, muteOutput=False, printTestCase=False, noGraphics=False)
34+
parser.add_option('--test-directory',
35+
dest = 'testRoot',
36+
default = 'test_cases',
37+
help = 'Root test directory which contains subdirectories corresponding to each question')
38+
parser.add_option('--student-code',
39+
dest = 'studentCode',
40+
default = projectParams.STUDENT_CODE_DEFAULT,
41+
help = 'comma separated list of student code files')
42+
parser.add_option('--code-directory',
43+
dest = 'codeRoot',
44+
default = "",
45+
help = 'Root directory containing the student and testClass code')
46+
parser.add_option('--test-case-code',
47+
dest = 'testCaseCode',
48+
default = projectParams.PROJECT_TEST_CLASSES,
49+
help = 'class containing testClass classes for this project')
50+
parser.add_option('--generate-solutions',
51+
dest = 'generateSolutions',
52+
action = 'store_true',
53+
help = 'Write solutions generated to .solution file')
54+
parser.add_option('--edx-output',
55+
dest = 'edxOutput',
56+
action = 'store_true',
57+
help = 'Generate edX output files')
58+
parser.add_option('--mute',
59+
dest = 'muteOutput',
60+
action = 'store_true',
61+
help = 'Mute output from executing tests')
62+
parser.add_option('--print-tests', '-p',
63+
dest = 'printTestCase',
64+
action = 'store_true',
65+
help = 'Print each test case before running them.')
66+
parser.add_option('--test', '-t',
67+
dest = 'runTest',
68+
default = None,
69+
help = 'Run one particular test. Relative to test root.')
70+
parser.add_option('--question', '-q',
71+
dest = 'gradeQuestion',
72+
default = None,
73+
help = 'Grade one particular question.')
74+
parser.add_option('--no-graphics',
75+
dest = 'noGraphics',
76+
action = 'store_true',
77+
help = 'No graphics display for pacman games.')
78+
(options, args) = parser.parse_args(argv)
79+
return options
80+
81+
82+
# confirm we should author solution files
83+
def confirmGenerate():
84+
print 'WARNING: this action will overwrite any solution files.'
85+
print 'Are you sure you want to proceed? (yes/no)'
86+
while True:
87+
ans = sys.stdin.readline().strip()
88+
if ans == 'yes':
89+
break
90+
elif ans == 'no':
91+
sys.exit(0)
92+
else:
93+
print 'please answer either "yes" or "no"'
94+
95+
96+
# TODO: Fix this so that it tracebacks work correctly
97+
# Looking at source of the traceback module, presuming it works
98+
# the same as the intepreters, it uses co_filename. This is,
99+
# however, a readonly attribute.
100+
def setModuleName(module, filename):
101+
functionType = type(confirmGenerate)
102+
classType = type(optparse.Option)
103+
104+
for i in dir(module):
105+
o = getattr(module, i)
106+
if hasattr(o, '__file__'): continue
107+
108+
if type(o) == functionType:
109+
setattr(o, '__file__', filename)
110+
elif type(o) == classType:
111+
setattr(o, '__file__', filename)
112+
# TODO: assign member __file__'s?
113+
#print i, type(o)
114+
115+
116+
#from cStringIO import StringIO
117+
118+
def loadModuleString(moduleSource):
119+
# Below broken, imp doesn't believe its being passed a file:
120+
# ValueError: load_module arg#2 should be a file or None
121+
#
122+
#f = StringIO(moduleCodeDict[k])
123+
#tmp = imp.load_module(k, f, k, (".py", "r", imp.PY_SOURCE))
124+
tmp = imp.new_module(k)
125+
exec moduleCodeDict[k] in tmp.__dict__
126+
setModuleName(tmp, k)
127+
return tmp
128+
129+
import py_compile
130+
131+
def loadModuleFile(moduleName, filePath):
132+
with open(filePath, 'r') as f:
133+
return imp.load_module(moduleName, f, "%s.py" % moduleName, (".py", "r", imp.PY_SOURCE))
134+
135+
136+
def readFile(path, root=""):
137+
"Read file from disk at specified path and return as string"
138+
with open(os.path.join(root, path), 'r') as handle:
139+
return handle.read()
140+
141+
142+
#######################################################################
143+
# Error Hint Map
144+
#######################################################################
145+
146+
# TODO: use these
147+
ERROR_HINT_MAP = {
148+
'q1': {
149+
"<type 'exceptions.IndexError'>": """
150+
We noticed that your project threw an IndexError on q1.
151+
While many things may cause this, it may have been from
152+
assuming a certain number of successors from a state space
153+
or assuming a certain number of actions available from a given
154+
state. Try making your code more general (no hardcoded indices)
155+
and submit again!
156+
"""
157+
},
158+
'q3': {
159+
"<type 'exceptions.AttributeError'>": """
160+
We noticed that your project threw an AttributeError on q3.
161+
While many things may cause this, it may have been from assuming
162+
a certain size or structure to the state space. For example, if you have
163+
a line of code assuming that the state is (x, y) and we run your code
164+
on a state space with (x, y, z), this error could be thrown. Try
165+
making your code more general and submit again!
166+
167+
"""
168+
}
169+
}
170+
171+
import pprint
172+
173+
def splitStrings(d):
174+
d2 = dict(d)
175+
for k in d:
176+
if k[0:2] == "__":
177+
del d2[k]
178+
continue
179+
if d2[k].find("\n") >= 0:
180+
d2[k] = d2[k].split("\n")
181+
return d2
182+
183+
184+
def printTest(testDict, solutionDict):
185+
pp = pprint.PrettyPrinter(indent=4)
186+
print "Test case:"
187+
for line in testDict["__raw_lines__"]:
188+
print " |", line
189+
print "Solution:"
190+
for line in solutionDict["__raw_lines__"]:
191+
print " |", line
192+
193+
194+
def runTest(testName, moduleDict, printTestCase=False, display=None):
195+
import testParser
196+
import testClasses
197+
for module in moduleDict:
198+
setattr(sys.modules[__name__], module, moduleDict[module])
199+
200+
testDict = testParser.TestParser(testName + ".test").parse()
201+
solutionDict = testParser.TestParser(testName + ".solution").parse()
202+
test_out_file = os.path.join('%s.test_output' % testName)
203+
testDict['test_out_file'] = test_out_file
204+
testClass = getattr(projectTestClasses, testDict['class'])
205+
206+
questionClass = getattr(testClasses, 'Question')
207+
question = questionClass({'max_points': 0}, display)
208+
testCase = testClass(question, testDict)
209+
210+
if printTestCase:
211+
printTest(testDict, solutionDict)
212+
213+
# This is a fragile hack to create a stub grades object
214+
grades = grading.Grades(projectParams.PROJECT_NAME, [(None,0)])
215+
testCase.execute(grades, moduleDict, solutionDict)
216+
217+
218+
# returns all the tests you need to run in order to run question
219+
def getDepends(testParser, testRoot, question):
220+
allDeps = [question]
221+
questionDict = testParser.TestParser(os.path.join(testRoot, question, 'CONFIG')).parse()
222+
if 'depends' in questionDict:
223+
depends = questionDict['depends'].split()
224+
for d in depends:
225+
# run dependencies first
226+
allDeps = getDepends(testParser, testRoot, d) + allDeps
227+
return allDeps
228+
229+
# get list of questions to grade
230+
def getTestSubdirs(testParser, testRoot, questionToGrade):
231+
problemDict = testParser.TestParser(os.path.join(testRoot, 'CONFIG')).parse()
232+
if questionToGrade != None:
233+
questions = getDepends(testParser, testRoot, questionToGrade)
234+
if len(questions) > 1:
235+
print 'Note: due to dependencies, the following tests will be run: %s' % ' '.join(questions)
236+
return questions
237+
if 'order' in problemDict:
238+
return problemDict['order'].split()
239+
return sorted(os.listdir(testRoot))
240+
241+
242+
# evaluate student code
243+
def evaluate(generateSolutions, testRoot, moduleDict, exceptionMap=ERROR_HINT_MAP, edxOutput=False, muteOutput=False,
244+
printTestCase=False, questionToGrade=None, display=None):
245+
# imports of testbench code. note that the testClasses import must follow
246+
# the import of student code due to dependencies
247+
import testParser
248+
import testClasses
249+
for module in moduleDict:
250+
setattr(sys.modules[__name__], module, moduleDict[module])
251+
252+
questions = []
253+
questionDicts = {}
254+
test_subdirs = getTestSubdirs(testParser, testRoot, questionToGrade)
255+
for q in test_subdirs:
256+
subdir_path = os.path.join(testRoot, q)
257+
if not os.path.isdir(subdir_path) or q[0] == '.':
258+
continue
259+
260+
# create a question object
261+
questionDict = testParser.TestParser(os.path.join(subdir_path, 'CONFIG')).parse()
262+
questionClass = getattr(testClasses, questionDict['class'])
263+
question = questionClass(questionDict, display)
264+
questionDicts[q] = questionDict
265+
266+
# load test cases into question
267+
tests = filter(lambda t: re.match('[^#~.].*\.test\Z', t), os.listdir(subdir_path))
268+
tests = map(lambda t: re.match('(.*)\.test\Z', t).group(1), tests)
269+
for t in sorted(tests):
270+
test_file = os.path.join(subdir_path, '%s.test' % t)
271+
solution_file = os.path.join(subdir_path, '%s.solution' % t)
272+
test_out_file = os.path.join(subdir_path, '%s.test_output' % t)
273+
testDict = testParser.TestParser(test_file).parse()
274+
if testDict.get("disabled", "false").lower() == "true":
275+
continue
276+
testDict['test_out_file'] = test_out_file
277+
testClass = getattr(projectTestClasses, testDict['class'])
278+
testCase = testClass(question, testDict)
279+
def makefun(testCase, solution_file):
280+
if generateSolutions:
281+
# write solution file to disk
282+
return lambda grades: testCase.writeSolution(moduleDict, solution_file)
283+
else:
284+
# read in solution dictionary and pass as an argument
285+
testDict = testParser.TestParser(test_file).parse()
286+
solutionDict = testParser.TestParser(solution_file).parse()
287+
if printTestCase:
288+
return lambda grades: printTest(testDict, solutionDict) or testCase.execute(grades, moduleDict, solutionDict)
289+
else:
290+
return lambda grades: testCase.execute(grades, moduleDict, solutionDict)
291+
question.addTestCase(testCase, makefun(testCase, solution_file))
292+
293+
# Note extra function is necessary for scoping reasons
294+
def makefun(question):
295+
return lambda grades: question.execute(grades)
296+
setattr(sys.modules[__name__], q, makefun(question))
297+
questions.append((q, question.getMaxPoints()))
298+
299+
grades = grading.Grades(projectParams.PROJECT_NAME, questions, edxOutput=edxOutput, muteOutput=muteOutput)
300+
if questionToGrade == None:
301+
for q in questionDicts:
302+
for prereq in questionDicts[q].get('depends', '').split():
303+
grades.addPrereq(q, prereq)
304+
305+
grades.grade(sys.modules[__name__], bonusPic = projectParams.BONUS_PIC)
306+
return grades.points
307+
308+
309+
310+
def getDisplay(graphicsByDefault, options=None):
311+
graphics = graphicsByDefault
312+
if options is not None and options.noGraphics:
313+
graphics = False
314+
if graphics:
315+
try:
316+
import graphicsDisplay
317+
return graphicsDisplay.PacmanGraphics(1, frameTime=.05)
318+
except ImportError:
319+
pass
320+
import textDisplay
321+
return textDisplay.NullGraphics()
322+
323+
324+
325+
326+
if __name__ == '__main__':
327+
options = readCommand(sys.argv)
328+
if options.generateSolutions:
329+
confirmGenerate()
330+
codePaths = options.studentCode.split(',')
331+
# moduleCodeDict = {}
332+
# for cp in codePaths:
333+
# moduleName = re.match('.*?([^/]*)\.py', cp).group(1)
334+
# moduleCodeDict[moduleName] = readFile(cp, root=options.codeRoot)
335+
# moduleCodeDict['projectTestClasses'] = readFile(options.testCaseCode, root=options.codeRoot)
336+
# moduleDict = loadModuleDict(moduleCodeDict)
337+
338+
moduleDict = {}
339+
for cp in codePaths:
340+
moduleName = re.match('.*?([^/]*)\.py', cp).group(1)
341+
moduleDict[moduleName] = loadModuleFile(moduleName, os.path.join(options.codeRoot, cp))
342+
moduleName = re.match('.*?([^/]*)\.py', options.testCaseCode).group(1)
343+
moduleDict['projectTestClasses'] = loadModuleFile(moduleName, os.path.join(options.codeRoot, options.testCaseCode))
344+
345+
346+
if options.runTest != None:
347+
runTest(options.runTest, moduleDict, printTestCase=options.printTestCase, display=getDisplay(True, options))
348+
else:
349+
evaluate(options.generateSolutions, options.testRoot, moduleDict,
350+
edxOutput=options.edxOutput, muteOutput=options.muteOutput, printTestCase=options.printTestCase,
351+
questionToGrade=options.gradeQuestion, display=getDisplay(options.gradeQuestion!=None, options))

0 commit comments

Comments
 (0)