First off, thank you everyone for this! This has been an amazing resource for my high school python classes.
I'm hoping someone can help me out here. I'm trying to build a prototype question that makes it easy for the question authors. I need my tests and the scratchpad code work together with injecting new code (dummy methods/classes) from a single source in the question authoring page.
The Issue
We use Phidgets in the classroom. While I can install the modules on the jobe server, the issue here is it should be interacting with hardware. Instead I want to inject my own classes into the code when either the "Try it Out" code is run in the scratchpad OR when the test is run.
Current Question Setup
UI parameters for Customization:
{
"wrapper_src": "prototypeextra",
"output_display_mode": "json",
"escape": true
}
Template
__saved_input__ = input
def input(prompt=''):
s = __saved_input__(prompt)
print(s)
return s
{{ QUESTION.globalextra }}
{{ STUDENT_ANSWER }}
SEPARATOR = "#<ab@17943918#@>#"
{% for TEST in TESTCASES %}
{{ TEST.testcode }}
{% if not loop.last %}
print(SEPARATOR)
{% endif %}
{% endfor %}
Prototype extra:
import sys, io, subprocess, ast, traceback, json, locale
locale.setlocale(locale.LC_ALL, "C.UTF-8")
MAX_OUTPUT_CHARS = 30000
student_code = """
{| ANSWER_CODE |}
{| SCRATCHPAD_CODE |}
"""
subproc_code = """
import sys
MAX_OUTPUT_CHARS = 30000
__saved_input__ = input
def input(prompt=''):
try:
line = __saved_input__()
except EOFError:
print(prompt, end = '')
sys.stderr.flush()
sys.stdout.flush()
sys.exit(42)
print(prompt, end='')
print(line)
return line
__saved_print__ = print
__output_chars__ = 0
def print(*params, **keyparams):
global __output_chars__
for param in params:
try:
__output_chars__ += len(str(param))
except:
pass
if __output_chars__ > 2 * MAX_OUTPUT_CHARS:
__saved_print__("\\\\n*** Excessive output. Job aborted ***", file=sys.stderr)
sys.exit(1)
else:
__saved_print__(*params, **keyparams)
"""
subproc_code += student_code
def truncated(s):
return s if len(s) < MAX_OUTPUT_CHARS else s[:MAX_OUTPUT_CHARS] + " ... (truncated)"
def check_syntax():
try:
ast.parse(student_code)
return ""
except SyntaxError:
catcher = io.StringIO()
traceback.print_exc(limit=0, file=catcher)
return catcher.getvalue()
stdout = ""
stderr = check_syntax()
if stderr == "": # No syntax errors
program_code = subproc_code
with open("prog.py", "w", encoding="utf-8") as outfile:
outfile.write(program_code)
proc = subprocess.Popen(
[sys.executable, "prog.py"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
)
try:
stdout, stderr = proc.communicate(timeout=3)
returncode = proc.returncode
except subprocess.TimeoutExpired:
proc.kill()
stdout, stderr = proc.communicate()
returncode = 13
else:
returncode = 1 # Syntax errors
output = {
"returncode": returncode,
"stdout": truncated(stdout),
"stderr": truncated(stderr),
"files": [],
}
print(json.dumps(output))
What I Can't Make Work
My tests will all pull the code entered in Global Extra without any issue. The trouble is I can't figure out how to pull Global Extra into the Prototype Extra so it also runs when "Try it Out" is pressed from the scratchpad.
End Goal
I want prototype extra saved as part of the prototype question so when que author uses it, any code in global extra is propagated to both the scratchpad runs and the tests (for example, injection of "fake" classes for hardware or just additional/preset code for everything).
I'm pretty new to all this but do know enough to get me into trouble. What am I missing here? Or am I just going about this all the wrong way?