Source code for gradelang.interpreter

""" Gradelang Interpreter.
"""
import io
import traceback
from contextlib import redirect_stdout
from functools import partial
from multiprocessing.pool import Pool

from gradelang.question import Question
from . import lexer, parser
from .state import state, NIL
from .walk import walk


[docs]def interpret(stream) -> None: """ Interprets the given stream. """ # reset the state object state.clean() # build the AST parser.parse(stream, lexer=lexer) # Execute Questions. _worker = partial(worker, setup=state.setup, teardown=state.teardown) with Pool() as p: state._questions = p.map(_worker, state.questions) # Generate Output. if 'json' in state.output: f = state.output['json'] if f != NIL: with open(f, 'w') as fp: fp.write(state.json()) else: print(state.json()) if 'markdown' in state.output: f = state.output['markdown'] if f != NIL: with open(f, 'w') as fp: fp.write(state.markdown()) else: print(state.markdown()) if NIL in state.output: print(state.report()) return
[docs]def worker(question: Question, setup: tuple, teardown: tuple) -> Question: """ Worker Process for questions. Passing setup and teardown explicitly because Windows uses `spawn` instead of `fork`, which means we lose the global state on Win. In either case, state is not returned from neither spawn or fork, so any changes made within this function cannot impact the caller. """ state.question = question output = io.StringIO() with redirect_stdout(output): try: walk(setup) walk(question.body) walk(teardown) except Exception as err: question.exception = repr(err) question.traceback = traceback.format_exc() finally: question.output = output.getvalue() question.workdir.cleanup() return question