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: Anyand 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 stringifiedThe 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 filewe 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 stringifiedSome 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.
- \$\begingroup\$Why the
rusttag?\$\endgroup\$Booboo– Booboo2025-05-28 17:00:11 +00:00CommentedMay 28 at 17:00
1 Answer1
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}"You mustlog in to answer this question.
Explore related questions
See similar questions with these tags.
