patternpythonMinor
Run multiple python codes and Check with answer data
Viewed 0 times
answerwithcodespythonmultipleandcheckdatarun
Problem
I wrote the code for my undergraduate TA jobs, to check assignment codes of students in Python class are correct or not.
Usage of the code is
It will write grades (range 0~100) to a file called
Usage of the code is
$ python [test01.py[,test02 , ...] answer.dat grade.mdIt will write grades (range 0~100) to a file called
grade.md."""
Testing Assginments
by kidkkr
03-16-2017
"""
import os
import sys
from difflib import SequenceMatcher
class ResultChecker:
""" Check python code result with answer data """
__slots__ = '_srcFiles', '_ansFile', '_gradeFile', '_grade', '_ans'
def __init__(self, srcFiles, ansFile, gradeFile):
self._srcFiles = srcFiles
self._ansFile = ansFile
self._ans = open(self._ansFile, 'r').read()
self._gradeFile = gradeFile
self._grade = "# Grade File\n"
def _run_code(self, src):
print('-----------running ' + src + '..-----------')
os.system('python ' + src + ' > result.swp')
res = open('./result.swp', 'r').read()
ratio = int(SequenceMatcher(None, res, self._ans).ratio() * 100)
self._grade += src + ' ' + str(ratio) + '\n'
print('result: ' + str(ratio))
def check(self):
for src in self._srcFiles:
self._run_code(src)
open(self._gradeFile, 'w').write(self._grade) # Write _grade to a file
print('Checking has completed.')
print(self._gradeFile + ":")
print(self._grade)
def __main__():
if len(sys.argv) < 2: # Check arguments
raise ValueError('')
srcFiles = sys.argv[1:-2]
ansFile = sys.argv[-2]
gradeFile = sys.argv[-1]
ResultChecker(srcFiles, ansFile, gradeFile).check()
if __name__ == '__main__':
__main__()Solution
Here is something I would improve:
-
use
Note the use of the special
And, because of the use of
-
use
- fix the variable naming - according to PEP8, use lower case and separate words with an underscore - e.g.
_grade_fileinstead of_gradeFile
-
use
argparse module instead of manually reading from sys.argv - you will get the auto-generated command-line help for free and it is generally more readable, structured and scalable. As a bonus, you may avoid checking the length of the sys.argv and reading the "files" into variables. Something along these lines:import argparse
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('--source-files', nargs="+", type=str)
parser.add_argument('--answer-file', type=argparse.FileType('r'))
parser.add_argument('--grade-file', type=argparse.FileType('w'))
return parser.parse_args()Note the use of the special
FileType argument type.And, because of the use of
nargs="+", you should change the way you pass python file names - instead of a comma-separated list, use space:$ python --source-files test01.py test02.py --answer-file answer.dat --grade-file grade.md-
use
with context manager when opening files- try
subprocess.Popen()instead ofos.system()- with "popen" you may not need to temporarily write the result to a swap file and read the standard out and error directly
- I understand that an underscore at the beginning of a variable name is a convention to mark "private variables", but, if the checker class would not be a part of a public-facing API, it would be okay to remove the underscores to improve on readability
Code Snippets
import argparse
def parse_arguments():
parser = argparse.ArgumentParser()
parser.add_argument('--source-files', nargs="+", type=str)
parser.add_argument('--answer-file', type=argparse.FileType('r'))
parser.add_argument('--grade-file', type=argparse.FileType('w'))
return parser.parse_args()$ python --source-files test01.py test02.py --answer-file answer.dat --grade-file grade.mdContext
StackExchange Code Review Q#157891, answer score: 6
Revisions (0)
No revisions yet.