6
\$\begingroup\$

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__() 
\$\endgroup\$
  • 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 Mar 16 '17 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 Mar 16 '17 at 19:33
6
\$\begingroup\$

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 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 of os.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
\$\endgroup\$
  • \$\begingroup\$ argparse module is cool. I hope I used to PEP8.. Thank you :) \$\endgroup\$ – kidkkr Mar 16 '17 at 19:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.