def validate_struct(ctx, location, obj, required_attrs, allowed_attrs): """ :arg required_attrs: an attribute validation list (see below) :arg allowed_attrs: an attribute validation list (see below) An attribute validation list is a list of elements, where each element is either a string (the name of the attribute), in which case the type of each attribute is not checked, or a tuple *(name, type)*, where type is valid as a second argument to :func:`isinstance`. """ present_attrs = set(name for name in dir(obj) if not name.startswith("_")) for required, attr_list in [ (True, required_attrs), (False, allowed_attrs), ]: for attr_rec in attr_list: if isinstance(attr_rec, tuple): attr, allowed_types = attr_rec else: attr = attr_rec allowed_types = None if attr not in present_attrs: if required: raise ValidationError("%s: attribute '%s' missing" % (location, attr)) else: present_attrs.remove(attr) val = getattr(obj, attr) is_markup = False if allowed_types == "markup": allowed_types = str is_markup = True if allowed_types == str: # Love you, too, Python 2. allowed_types = (str, unicode) if not isinstance(val, allowed_types): raise ValidationError("%s: attribute '%s' has " "wrong type: got '%s', expected '%s'" % (location, attr, type(val).__name__, html_escape(str(allowed_types)))) if is_markup: validate_markup(ctx, "%s: attribute %s" % (location, attr), val) if present_attrs: raise ValidationError("%s: extraneous attribute(s) '%s'" % (location, ",".join(present_attrs)))
def grade(self, page_context, page_data, answer_data, grade_data): from courseflow.utils import html_escape if hasattr(self.page_desc, "correct_code"): correct_answer = ( "The following code is a valid answer:<pre>%s</pre>" % html_escape(self.page_desc.correct_code)) else: correct_answer = "" if answer_data is None: return AnswerFeedback(correctness=0, feedback="No answer provided.", correct_answer=correct_answer, normalized_answer=None) user_code = answer_data["answer"] # {{{ request run run_req = {"compile_only": False, "user_code": user_code} def transfer_attr(name): if hasattr(self.page_desc, name): run_req[name] = getattr(self.page_desc, name) transfer_attr("setup_code") transfer_attr("names_for_user") transfer_attr("names_from_user") transfer_attr("test_code") try: response_dict = request_python_run(run_req, run_timeout=self.page_desc.timeout) except: from traceback import format_exc response_dict = { "result": "uncaught_error", "message": "Error connecting to container", "traceback": "".join(format_exc()), } # }}} # {{{ send email if the grading code broke if response_dict["result"] in [ "uncaught_error", "setup_compile_error", "setup_error", "test_compile_error", "test_error"]: error_msg_parts = ["RESULT: %s" % response_dict["result"]] for key, val in sorted(response_dict.items()): if key != "result" and val: error_msg_parts.append("-------------------------------------") error_msg_parts.append(key) error_msg_parts.append("-------------------------------------") error_msg_parts.append(val) error_msg_parts.append("-------------------------------------") error_msg_parts.append("user code") error_msg_parts.append("-------------------------------------") error_msg_parts.append(user_code) error_msg_parts.append("-------------------------------------") error_msg = "\n".join(error_msg_parts) from django.template.loader import render_to_string message = render_to_string("course/broken-code-question-email.txt", { "page_id": self.page_desc.id, "course": page_context.course, "error_message": error_msg, }) from django.core.mail import send_mail from django.conf import settings send_mail("[%s] code question execution failed" % page_context.course.identifier, message, settings.ROBOT_EMAIL_FROM, recipient_list=[page_context.course.email]) # }}} from courseflow.utils import dict_to_struct response = dict_to_struct(response_dict) feedback_bits = [] if response.result == "success": pass elif response.result in [ "uncaught_error", "setup_compile_error", "setup_error", "test_compile_error", "test_error"]: feedback_bits.append( "<p>The grading code failed. Sorry about that. " "The staff has been informed, and if this problem is due " "to an issue with the grading code, " "it will be fixed as soon as possible. " "In the meantime, you'll see a traceback " "below that may help you figure out what went wrong.</p>") elif response.result == "timeout": feedback_bits.append( "<p>Your code took too long to execute. The problem " "specifies that your code may take at most %s seconds to run. " "It took longer than that and was aborted.</p>" % self.page_desc.timeout) elif response.result == "user_compile_error": feedback_bits.append( "<p>Your code failed to compile. An error message is below.</p>") elif response.result == "user_error": feedback_bits.append( "<p>Your code failed with an exception. " "A traceback is below.</p>") else: raise RuntimeError("invalid cfrunpy result: %s" % response.result) if hasattr(response, "points"): correctness = response.points feedback_bits.append( "<p><b>%s</b></p>" % get_auto_feedback(correctness)) else: correctness = None if hasattr(response, "feedback") and response.feedback: feedback_bits.append( "<p>Here is some feedback on your code:" "<ul>%s</ul></p>" % "".join( "<li>%s</li>" % html_escape(fb_item) for fb_item in response.feedback)) if hasattr(response, "traceback") and response.traceback: feedback_bits.append( "<p>This is the exception traceback:" "<pre>%s</pre></p>" % html_escape(response.traceback)) print repr(response.traceback) if hasattr(response, "stdout") and response.stdout: feedback_bits.append( "<p>Your code printed the following output:<pre>%s</pre></p>" % html_escape(response.stdout)) if hasattr(response, "stderr") and response.stderr: feedback_bits.append( "<p>Your code printed the following error messages:" "<pre>%s</pre></p>" % html_escape(response.stderr)) return AnswerFeedback( correctness=correctness, correct_answer=correct_answer, feedback="\n".join(feedback_bits))