diff --git a/common/lib/capa/capa/capa_problem.py b/common/lib/capa/capa/capa_problem.py index 344843ba10..4cffc48bea 100644 --- a/common/lib/capa/capa/capa_problem.py +++ b/common/lib/capa/capa/capa_problem.py @@ -22,6 +22,7 @@ import random import re import scipy import struct +import sys from lxml import etree from xml.sax.saxutils import unescape @@ -78,7 +79,7 @@ class LoncapaProblem(object): - id (string): identifier for this problem; often a filename (no spaces) - state (dict): student state - seed (int): random number generator seed (int) - - system (I4xSystme): I4xSystem instance which provides OS, rendering, and user context + - system (I4xSystem): I4xSystem instance which provides OS, rendering, and user context ''' @@ -196,7 +197,7 @@ class LoncapaProblem(object): Thus, for example, input_ID123 -> ID123, and input_fromjs_ID123 -> fromjs_ID123 - Calles the Response for each question in this problem, to do the actual grading. + Calls the Response for each question in this problem, to do the actual grading. ''' self.student_answers = answers oldcmap = self.correct_map # old CorrectMap @@ -276,6 +277,34 @@ class LoncapaProblem(object): parent.remove(inc) log.debug('Included %s into %s' % (file, self.problem_id)) + def _extract_system_path(self, script): + ''' + Extracts and normalizes additional paths for code execution. + For now, there's a default path of data/course/code; this may be removed + at some point. + ''' + + DEFAULT_PATH = ['code'] + + # Separate paths by :, like the system path. + raw_path = script.get('system_path', '').split(":") + DEFAULT_PATH + + # find additional comma-separated modules search path + path = [] + + for dir in raw_path: + + if not dir: + continue + + # path is an absolute path or a path relative to the data dir + dir = os.path.join(self.system.filestore.root_path, dir) + abs_dir = os.path.normpath(dir) + log.debug("appending to path: %s" % abs_dir) + path.append(abs_dir) + + return path + def _extract_context(self, tree, seed=struct.unpack('i', os.urandom(4))[0]): # private ''' Extract content of from the problem.xml file, and exec it in the @@ -291,7 +320,20 @@ class LoncapaProblem(object): context['the_lcp'] = self # pass instance of LoncapaProblem in context['script_code'] = '' - for script in tree.findall('.//script'): + self._execute_scripts(tree.findall('.//script'), context) + + return context + + def _execute_scripts(self, scripts, context): + ''' + Executes scripts in the given context. + ''' + original_path = sys.path + + for script in scripts: + + sys.path = original_path + self._extract_system_path(script) + stype = script.get('type') if stype: if 'javascript' in stype: @@ -308,7 +350,8 @@ class LoncapaProblem(object): except Exception: log.exception("Error while execing script code: " + code) raise responsetypes.LoncapaProblemError("Error while executing script code") - return context + finally: + sys.path = original_path def _extract_html(self, problemtree): # private '''