Usage of Django template language as template for a program

I’m considering using Django template system for a pretty peculiar use case and I’m looking for some guidance.

I have an app which allows users to solve programming problems by submitting a code solution. The app allows users to write programs in several languages, including Python, JS/TS, and C.

Each problem is associated with a suite of test cases meant to verify the correctness of the submitted solution. My app combines the user code and the test cases’ code into a program that is run by a sandboxed environment.

Right now, the way the submitted code and the test cases are combined is hard-coded and looks roughly like this:

def get_testcase_execution_block(
    testcase: ExerciseTestCase, execution_results_list_identifier: str
) -> str:
    results_dict = get_random_identifier()
    exception_identifier = get_random_identifier()
    return (
        f"try:\n"
        + f"    {testcase.code}\n"
        + f"    {results_dict} = {{'passed': True, 'id': {testcase.pk}}}\n"
        + f"except Exception as {exception_identifier}:\n"
        + f"    ex_type, ex_value, ex_traceback = sys.exc_info()\n"
        + f"    trace_back = traceback.extract_tb(ex_traceback)\n"
        + f"    stack_trace = list()\n"
        + f"    for trace in trace_back:\n"
        + f'        stack_trace.append("File : %s , Line : %d, Func.Name : %s, Message : %s" % (trace[0], trace[1], trace[2], trace[3]))\n'
        + f"    {results_dict} = {{'passed': False, 'id': {testcase.pk}, 'error': ex_type.__name__ + ': ' + str(ex_value) + ' (' + str(stack_trace) +')'}}\n"
        + f"{execution_results_list_identifier}.append({results_dict})\n"
    )


def get_python_program_for_vm(code: str, testcases: ExerciseTestCase) -> str:
    execution_results_list_identifier = get_random_identifier()
    testcases_str = "".join(
        [
            get_testcase_execution_block(t, execution_results_list_identifier)
            for t in testcases
        ]
    )
    return (
        "import sys\n"
        + "import traceback\n"
        + "import json\n"
        + f"{execution_results_list_identifier}=[]\n"  # declare list to hold test case results
        + f"{code}\n"  # inline submitted code
        + f"{testcases_str}\n"  # run test cases in try - except blocks
        + f"print(json.dumps({execution_results_list_identifier}))"  # print out the result list
    )

What this essentially does it it creates a string with some imports, then injects the user code, then adds a series of try - except blocks, one for each test case (imagine a test case being some assert that will therefore raise an exception if the code fails), in which a dict that represents the details of the test execution is pushed to a list which is ultimately printed to stdout and collected by the process in which Django runs.

This is fine for most cases, but I’d like to give the authors more flexibility as to how they want their programs to be tested.

Instead of hard-coding a string like this, I’d like the admin to be able to specify a template for combining the user code and test cases. For a C program, it might look like this (this is not Django template language, it’s just to give an idea):

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <stdbool.h>
#include <math.h>

{{ USER_CODE }}

int main() {
{% for TEST in TESTCASES %}
   {
    {{ TEST.testcode }};
   }
{% endfor %}
    return 0;
}

My question is: would the Django template language be appropriate for this?

My idea is to use an admin-defined Django template to ultimately render to a plain string, instead of an HTML page like it’s usually employed. Some concerns are escaping, properly (de)indenting injected code, and simplicity of use for those who need to create such templates.

In a functionally similar situation, we use the Django template engine to render emails and CSV files. It’s quite suitable for this type of application. (See the first paragraph at The Django template language | Django documentation | Django)

You can effectively wrap the entire template inside the autoescape tag to prevent html-like data from being escaped.

Getting it to look right when formatted takes a little getting used to - for the most part, the template engine keeps all the newlines from the original text, so the template itself won’t be formatted as nicely if you’re going for a precise “look” to the rendered text.

1 Like

There’s only one thing I have some doubts about:

as you can see from my examples, I am generating some random identifiers to be used as variable names. This is necessary to avoid unintentional name collisions with user defined variables (and somewhat discourage intentional name clashing, unless you’re REALLY motivated to find the variable names).

I am unsure how I would achieve that using the template language: I would have to have both a way to generate the identifier in the template, and be able to refer to it later on.

I haven’t gone in depth as to what the limits of this language are, but would something like this be possible?

{{ IDENTIFIERS[0] = get_identifier() }} = {}
...
{{ IDENTIFIERS[0]}}['details'] = '...'
...
results.append({{IDENTIFIERS[0]}})

and have a result such as, for example:

wqiesdnfrgiwegjiooe = {}
...
wqiesdnfrgiwegjiooe['details'] = '...'
...
results.append(wqiesdnfrgiwegjiooe)

So I would need to be able to both generate such identifiers and tie them to the context.

No, that’s outside the scope of the template.

You would want to create those identifiers in your code, and add them to the context to be rendered. You would then render those identifiers within the template.
e.g.:
context['identifier_1'] = 'gqcyzxm'

Then in your template:

{{ identifier_1 }} = {}
{{ identifier_1 }}['details'] = '...'
results.append({{identifier_1}})

renders as:

gqcyzxm = {}
gqcyzxm['details'] = '...'
results.append(gqcyzxm)
1 Like

Okay, so basically I am either stuck at providing the user with a pre-defined number of identifiers they can use, or I could allow them to refer to arbitrarily many identifiers and then, before compiling the template string, process the template text and match a regex such as identifier_\d+, generate a variable for each match, and add it to the context.

I feel like that would work, are there any downsides to it that you can think of?

Or create a list of identifiers and refer to them by numeric index, or create a callable to allow for a more “dynamic” reference. (Keep in mind all your options list at Variables)

Nope - the sky’s the limit here.

1 Like