HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Run multiple python codes and Check with answer data

Submitted by: @import:stackexchange-codereview··
0
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

$ 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__()

Solution

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

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.md

Context

StackExchange Code Review Q#157891, answer score: 6

Revisions (0)

No revisions yet.