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
$ python [test01.py[,test02 , ...] answer.dat grade.md
It 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__()
-
2\$\begingroup\$ Note that you should make sure you're being safe with untrusted scripts. Even if they're not malicious a student could break things that could cause future tests to fail or bigger problems. You should sandbox each run, either using something from here wiki.python.org/moin/Asking%20for%20Help/… or simply using a vm \$\endgroup\$Adam Martin– Adam Martin2017年03月16日 11:45:59 +00:00Commented Mar 16, 2017 at 11:45
-
\$\begingroup\$ I could not reached to that. It will be possible because I run codes directly in my shell.. Thank you for the advice! \$\endgroup\$kidkkr– kidkkr2017年03月16日 19:33:36 +00:00Commented Mar 16, 2017 at 19:33
1 Answer 1
Here is something I would improve:
- fix the variable naming - according to PEP8, use lower case and separate words with an underscore - e.g.
_grade_file
instead of_gradeFile
use
argparse
module instead of manually reading fromsys.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 thesys.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
-
\$\begingroup\$
argparse
module is cool. I hope I used to PEP8.. Thank you :) \$\endgroup\$kidkkr– kidkkr2017年03月16日 19:35:45 +00:00Commented Mar 16, 2017 at 19:35