Skip to content

Commit

Permalink
Allow grader scripts to be loaded through dragon-runner cli
Browse files Browse the repository at this point in the history
  • Loading branch information
JustinMeimar committed Nov 12, 2024
1 parent bee1813 commit c4b42f7
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 24 deletions.
65 changes: 43 additions & 22 deletions dragon_runner/cli.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
from typing import NamedTuple, List
import argparse
import os
from typing import NamedTuple
import shlex

class CLIArgs(NamedTuple):
def parse_script_args(arg_string: str) -> List[str]:
"""
Parse a quoted string into a list of arguments using shlex
"""
if not arg_string:
return []
return shlex.split(arg_string)

class CLIArgs(NamedTuple):
config_file: str
output_file: str
failure_log: str
debug_package: str
debug_package: str
mode: str
timeout: float
time: bool
verbosity: int
verify: bool
script_args: List[str]

def __repr__(self) -> str:
return (
Expand All @@ -24,18 +34,23 @@ def __repr__(self) -> str:
f" Time: {self.time}\n"
f" Output file: {self.output_file}\n"
f" Verbosity: {self.verbosity}\n"
f" Verify: {self.verify}"
f" Verify: {self.verify}\n"
f" Script Args: {' '.join(self.script_args)}"
)

def parse_cli_args() -> CLIArgs:
parser = argparse.ArgumentParser(description="CMPUT 415 testing utility")

parser.add_argument("config_file",
help="Path to the tester JSON configuration file.")

# Make config_file optional
parser.add_argument("config_file", nargs="?", default=None,
help="Path to the tester JSON configuration file (required for regular and grade modes).")

parser.add_argument("--mode", dest="mode", default="regular",
help="run in regular, grade or script mode")

parser.add_argument("--script-args", type=parse_script_args, default=[],
help='Arguments to pass to the script (quote the entire string, e.g. --script-args="arg1 arg2")')

parser.add_argument("--fail-log", dest="failure_log",
help="Log the testcases the solution compiler fails.")

Expand All @@ -56,24 +71,30 @@ def parse_cli_args() -> CLIArgs:

parser.add_argument("-o", "--output", metavar="FILE",
help="Direct the output of dragon-runner to FILE")

args = parser.parse_args()
if not os.path.isfile(args.config_file):
parser.error(f"The config file {args.config_file} does not exist.")
if args.mode == "grade" and not bool(args.failure_log):
parser.error("Failure log must be supplied when using grade mode.")

# Check if config file is required based on mode
if args.mode in ["regular", "grade"]:
if not args.config_file:
parser.error(f"Config file is required for {args.mode} mode")
if not os.path.isfile(args.config_file):
parser.error(f"The config file {args.config_file} does not exist.")
if args.mode == "grade" and (not bool(args.failure_log) or not bool(args.output)):
parser.error("Failure log and ouput file must be supplied when using grade mode.")

if args.verbosity > 0:
os.environ["DEBUG"] = str(args.verbosity)

return CLIArgs(
config_file = args.config_file,
mode = args.mode,
failure_log = args.failure_log,
timeout = args.timeout,
debug_package = args.debug_package,
output_file = args.output,
time = args.time,
verbosity = args.verbosity,
verify = args.verify
config_file = args.config_file,
mode = args.mode,
failure_log = args.failure_log,
timeout = args.timeout,
debug_package = args.debug_package,
output_file = args.output,
time = args.time,
verbosity = args.verbosity,
verify = args.verify,
script_args = args.script_args
)

8 changes: 8 additions & 0 deletions dragon_runner/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from dragon_runner.config import load_config
from dragon_runner.log import log, log_multiline
from dragon_runner.harness import TestHarness
from dragon_runner.scripts.loader import Loader

# initialize terminal colors
init(autoreset=True)
Expand All @@ -11,6 +12,13 @@ def main():
# parse and verify the CLI arguments
args: CLIArgs = parse_cli_args()
log(args, level=1)

# dragon-runner can also be used as a frontend for grading scripts
if args.mode not in ["regular", "grade"]:
print(f"Use dragon-runner as a loader for script: {args.mode}")
loader = Loader(args.mode, args.script_args)
loader.run()
return 0

# parse and verify the config
config = load_config(args.config_file, args)
Expand Down
Empty file.
13 changes: 13 additions & 0 deletions dragon_runner/scripts/loader.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import subprocess
from typing import List

class Loader:

def __init__(self, script: str, cmd: List[str]):
self.script = script
self.cmd = cmd

def run(self):

print(f"Running: {self.script} with args {self.cmd}")
subprocess.run(self.cmd, shell=True)
3 changes: 2 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ def create_cli_args(**kwargs) -> CLIArgs:
timeout = kwargs.get('timeout', None),
time = kwargs.get('time', None),
verbosity = kwargs.get('verbosity', None),
verify = kwargs.get('verify', None)
verify = kwargs.get('verify', None),
script_args = kwargs.get('script_args', None)
)

@pytest.fixture(scope="session")
Expand Down
2 changes: 1 addition & 1 deletion tests/test_grader.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def test_grader_config(config_factory, cli_factory):
config : Config = config_factory("ConfigGrade.json")
args : CLIArgs = cli_factory(**{
"output_file": "Grades.csv",
"failure_file": "Failures.txt",
"failure_log": "Failures.txt",
"timeout": 1
})

Expand Down

0 comments on commit c4b42f7

Please sign in to comment.