6
\$\begingroup\$

I am creating a website with coding questions. When a user submits a code for a question I use the contents of the question to create a python file and then I send it to the piston API for evaluation.

I use this function to create the python files content as string

fn inject_code(question_id: i32, content: String, db_pool: DbPool) -> String {    let question= get_single_question(question_id, db_pool).expect("Expected to find question");        let imports= std::fs::read_to_string("injections/function_top.py").unwrap();    let change_name = format!("__some_function = Solution.{}", question.function_name);    let cases = question.cases;    let py_runner = std::fs::read_to_string("injections/function.py").unwrap();     format!("{imports}\n\n{content}\n\n{change_name}\n\n{cases}\n\n{py_runner}")}

Where injection/function_top looks like:

from typing import *from dataclasses import dataclass@dataclassclass TestCase:    inputs: tuple    expected: Any

and injection/function looks like:

import iofrom contextlib import redirect_stdoutimport jsontest_results = []for case_id, test_case in enumerate(cases):    args = test_case.inputs    result = test_case.expected    with io.StringIO() as buf, redirect_stdout(buf):        error = None        correct = False        try:            correct = __some_function(*args) == result        except Exception as e:            error = e        function_stdout = buf.getvalue()        case_signature = f"{str(args)} -> {str(result)}"        test_results.append(            {"is_correct": correct, "case_stdout": function_stdout, "error": error, "case_signature": case_signature}        )print(json.dumps(test_results, default=str))  # Ensures exceptions are stringified

The cases are stored as native python files such as this:

cases = [    TestCase(inputs=([], []), expected=[]),    TestCase(inputs=([1], [2]), expected=[1, 2]),    TestCase(inputs=([2], [1]), expected=[2, 1]),    TestCase(inputs=([3, 2, 1], []), expected=[3, 2, 1]),    TestCase(inputs=([], [1, 2, 3]), expected=[1, 2, 3]),    TestCase(inputs=([2, 3], [6, 4, 2]), expected=[2, 3, 6, 4, 2]),]

and if we run this line of code for debug:

let injected_code = inject_code(id, content, pool);std::fs::write("test.py", &injected_code).unwrap(); // debug the created python file

we get test.py (this is the file that would be sent to the piston API)

from typing import *from dataclasses import dataclass@dataclassclass TestCase:    inputs: tuple    expected: Anyclass Solution:    def concat(arr_1: List[int], arr_2: List[int]):        return arr_1 + arr_2__some_function = Solution.concatcases = [    TestCase(inputs=([], []), expected=[]),    TestCase(inputs=([1], [2]), expected=[1, 2]),    TestCase(inputs=([2], [1]), expected=[2, 1]),    TestCase(inputs=([3, 2, 1], []), expected=[3, 2, 1]),    TestCase(inputs=([], [1, 2, 3]), expected=[1, 2, 3]),    TestCase(inputs=([2, 3], [6, 4, 2]), expected=[2, 3, 6, 4, 2]),]import iofrom contextlib import redirect_stdoutimport jsontest_results = []for case_id, test_case in enumerate(cases):    args = test_case.inputs    result = test_case.expected    with io.StringIO() as buf, redirect_stdout(buf):        error = None        correct = False        try:            correct = __some_function(*args) == result        except Exception as e:            error = e        function_stdout = buf.getvalue()        case_signature = f"{str(args)} -> {str(result)}"        test_results.append(            {"is_correct": correct, "case_stdout": function_stdout, "error": error, "case_signature": case_signature}        )print(json.dumps(test_results, default=str))  # Ensures exceptions are stringified

Some explanation:I am printing the final result because piston API only returns the program output. It is printed as JSON so that I can easily send it to my frontend. I am redirecting the stdout while the function is tested so that the user can see what was printed when the test case ran. I started by using a dictionary for my test cases but since the keys need to be hashable that turned out to be a problem.

I wanted the test cases to be written in native python so that any valid python object could be the answer or argument to the test case.

Is there any way I can improve this system? One mini problem that I have right now is that when creating a case my editor complains that "TestCase" is not defined.

ggorlen's user avatar
ggorlen
4,1972 gold badges19 silver badges28 bronze badges
askedMay 21 at 11:17
Doruk's user avatar
\$\endgroup\$
1
  • \$\begingroup\$Why therust tag?\$\endgroup\$CommentedMay 28 at 17:00

1 Answer1

5
\$\begingroup\$

name mangling

    let change_name = format!("__some_function = Solution.{}", question.function_name);

I don't understand the leading dunder there.Name manglingis seldom helpful.Recommend you just use_some_function --a_private variable.

Also, instead of the rather vague "some", perhapsyou'd prefer_target_function.

wildcard import

from typing import *

That module includesname,stat, and otherinnocuous sounding symbols that might causeconfusion for a contestant.For example, typos could lead to surprising results.Recommend that you only import the several symbolsthat you actually want, and make contestantsresponsible for importing what they needin their submitted source code.

Also, please use$ isort *.py to organizeyour imports in the way thatPEP 8recommends.

old-style annotations

    def concat(arr_1: List[int], arr_2: List[int]):

Prefer the following.It is modern notation, andit doesn't needfrom typing import List.

    def concat(arr_1: list[int], arr_2: list[int]):

test module

This isn't the end of the world:

std::fs::write("test.py", &injected_code).unwrap();

But it's usually not a terrific idea to namea moduletest, sinceimport test is alreadyprovided by the Batteries Included builtin libraries.Prefertest1 or some other identifier,to avoid confusion.

f-strings

I don't understand this line.

        case_signature = f"{str(args)} -> {str(result)}"

Note that formatting with an f-stringwillalready invokestr().What you wanted to write was simply:

        case_signature = f"{args} -> {result}"
Schism's user avatar
Schism
3,64517 silver badges31 bronze badges
answeredMay 21 at 16:42
J_H's user avatar
\$\endgroup\$

You mustlog in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.