From 47e47303dca6cf6b81272a5a8882951d88c8d8b8 Mon Sep 17 00:00:00 2001 From: Will Daly Date: Tue, 26 Mar 2013 13:36:27 -0400 Subject: [PATCH] Refactored CustomResponse to use the same private func to handle all errors related to execution of python code. CustomResponse now returns subclasses of Exception instead of general Exceptions CustomResponse no longer includes tracebacks in the exceptions it raises (and shows to students) --- common/lib/capa/capa/responsetypes.py | 37 ++++++++++++------- .../lib/capa/capa/tests/test_responsetypes.py | 17 +++++++-- 2 files changed, 38 insertions(+), 16 deletions(-) diff --git a/common/lib/capa/capa/responsetypes.py b/common/lib/capa/capa/responsetypes.py index 8ab716735c..a69c26572d 100644 --- a/common/lib/capa/capa/responsetypes.py +++ b/common/lib/capa/capa/responsetypes.py @@ -1072,13 +1072,11 @@ def sympy_check2(): correct = self.context['correct'] messages = self.context['messages'] overall_message = self.context['overall_message'] + except Exception as err: - print "oops in customresponse (code) error %s" % err - print "context = ", self.context - print traceback.format_exc() - # Notify student - raise StudentInputError( - "Error: Problem could not be evaluated with your input") + self._handle_exec_exception(err) + pass + else: # self.code is not a string; assume its a function @@ -1105,13 +1103,9 @@ def sympy_check2(): nargs, args, kwargs)) ret = fn(*args[:nargs], **kwargs) + except Exception as err: - log.error("oops in customresponse (cfn) error %s" % err) - # print "context = ",self.context - log.error(traceback.format_exc()) - raise Exception("oops in customresponse (cfn) error %s" % err) - log.debug( - "[courseware.capa.responsetypes.customresponse.get_score] ret = %s" % ret) + self._handle_exec_exception(err) if type(ret) == dict: @@ -1157,7 +1151,7 @@ def sympy_check2(): # Raise an exception else: log.error(traceback.format_exc()) - raise Exception( + raise LoncapaProblemError( "CustomResponse: check function returned an invalid dict") # The check function can return a boolean value, @@ -1227,6 +1221,23 @@ def sympy_check2(): return {self.answer_ids[0]: self.expect} return self.default_answer_map + def _handle_exec_exception(self, err): + ''' + Handle an exception raised during the execution of + custom Python code. + + Raises a StudentInputError + ''' + + # Log the error if we are debugging + msg = 'Error occurred while evaluating CustomResponse: %s' % str(err) + log.debug(msg) + log.debug(traceback.format_exc()) + + # Notify student + raise StudentInputError( + "Error: Problem could not be evaluated with your input") + #----------------------------------------------------------------------------- diff --git a/common/lib/capa/capa/tests/test_responsetypes.py b/common/lib/capa/capa/tests/test_responsetypes.py index 0c007f83b2..ac50e6defc 100644 --- a/common/lib/capa/capa/tests/test_responsetypes.py +++ b/common/lib/capa/capa/tests/test_responsetypes.py @@ -13,6 +13,7 @@ import textwrap from . import test_system import capa.capa_problem as lcp +from capa.responsetypes import LoncapaProblemError, StudentInputError from capa.correctmap import CorrectMap from capa.util import convert_files_to_filenames from capa.xqueue_interface import dateformat @@ -853,7 +854,7 @@ class CustomResponseTest(ResponseTest): # Message is interpreted as an "overall message" self.assertEqual(correct_map.get_overall_message(), 'Message text') - def test_script_exception(self): + def test_script_exception_function(self): # Construct a script that will raise an exception script = textwrap.dedent(""" @@ -864,7 +865,17 @@ class CustomResponseTest(ResponseTest): problem = self.build_problem(script=script, cfn="check_func") # Expect that an exception gets raised when we check the answer - with self.assertRaises(Exception): + with self.assertRaises(StudentInputError): + problem.grade_answers({'1_2_1': '42'}) + + def test_script_exception_inline(self): + + # Construct a script that will raise an exception + script = 'raise Exception("Test")' + problem = self.build_problem(answer=script) + + # Expect that an exception gets raised when we check the answer + with self.assertRaises(StudentInputError): problem.grade_answers({'1_2_1': '42'}) def test_invalid_dict_exception(self): @@ -878,7 +889,7 @@ class CustomResponseTest(ResponseTest): problem = self.build_problem(script=script, cfn="check_func") # Expect that an exception gets raised when we check the answer - with self.assertRaises(Exception): + with self.assertRaises(LoncapaProblemError): problem.grade_answers({'1_2_1': '42'})