def _test(state, incorrect_msg, exact_names, tv_name, highlight_name): rep = Reporter.active_reporter # get parts for testing from state # TODO: this could be rewritten to use check_part_index -> has_equal_part, etc.. stu_vars = state.student_parts[tv_name] sol_vars = state.solution_parts[tv_name] stu_target = state.student_parts.get( highlight_name) # TODO should be node? # variables exposed to messages d = {'stu_vars': stu_vars, 'sol_vars': sol_vars, 'num_vars': len(sol_vars)} if exact_names: # message for wrong iter var names _msg = state.build_message(incorrect_msg, d) # test rep.do_test(EqualTest(stu_vars, sol_vars, Feedback(_msg, stu_target))) else: # message for wrong number of iter vars _msg = state.build_message(incorrect_msg, d) # test rep.do_test( EqualTest(len(stu_vars), len(sol_vars), Feedback(_msg, stu_target))) return state
def has_iter_vars(incorrect_iter_vars_msg, exact_names=False, state=None): rep = Reporter.active_reporter # get parts for testing from state # TODO: this could be rewritten to use check_part_index -> has_equal_part, etc.. stu_vars = state.student_parts['_target_vars'] sol_vars = state.solution_parts['_target_vars'] stu_target = state.student_parts['target'] # variables exposed to messages d = {'stu_vars': stu_vars, 'sol_vars': sol_vars, 'num_vars': len(sol_vars)} if exact_names: # message for wrong iter var names _msg = state.build_message( incorrect_iter_vars_msg or MSG_INCORRECT_ITER_VARS, d) # test rep.do_test(EqualTest(stu_vars, sol_vars, Feedback(_msg, stu_target))) else: # message for wrong number of iter vars _msg = state.build_message( incorrect_iter_vars_msg or MSG_INCORRECT_NUM_ITER_VARS, d) # test rep.do_test( EqualTest(len(stu_vars), len(sol_vars), Feedback(_msg, stu_target))) return state
def parse_ext(x): rep = Reporter.active_reporter res = None try: res = ast.parse(x) # enrich tree with end lines and end columns utils_ast.mark_text_ranges(res, x + '\n') except IndentationError as e: rep.set_tag("fun", "indentation_error") e.filename = "script.py" # no line info for now rep.feedback = Feedback("Your code could not be parsed due to an error in the indentation:<br>`%s.`" % str(e)) rep.failed_test = True except SyntaxError as e: rep.set_tag("fun", "syntax_error") e.filename = "script.py" # no line info for now rep.feedback = Feedback("Your code can not be executed due to a syntax error:<br>`%s.`" % str(e)) rep.failed_test = True # Can happen, can't catch this earlier because we can't differentiate between # TypeError in parsing or TypeError within code (at runtime). except: rep.set_tag("fun", "other_error") rep.feedback.message = "Something went wrong while parsing your code." rep.failed_test = True finally: if (res is None): res = False return(res)
def _test(state, incorrect_msg, exact_names, tv_name, highlight_name): # get parts for testing from state # TODO: this could be rewritten to use check_part_index -> has_equal_part, etc.. stu_vars = state.student_parts[tv_name] sol_vars = state.solution_parts[tv_name] child_state = state.to_child( student_ast=state.student_parts.get(highlight_name), solution_ast=state.solution_parts.get(highlight_name), ) # variables exposed to messages d = {"stu_vars": stu_vars, "sol_vars": sol_vars, "num_vars": len(sol_vars)} if exact_names: # message for wrong iter var names _msg = state.build_message(incorrect_msg, d) # test state.do_test( EqualTest(stu_vars, sol_vars, Feedback(_msg, child_state))) else: # message for wrong number of iter vars _msg = state.build_message(incorrect_msg, d) # test state.do_test( EqualTest(len(stu_vars), len(sol_vars), Feedback(_msg, child_state))) return state
def parse_external(self, code): res = (None, None) try: return self.ast_dispatcher.parse(code) except IndentationError as e: e.filename = "script.py" # no line info for now self.report( Feedback( "Your code could not be parsed due to an error in the indentation:<br>`%s.`" % str(e))) except SyntaxError as e: e.filename = "script.py" # no line info for now self.report( Feedback( "Your code can not be executed due to a syntax error:<br>`%s.`" % str(e))) # Can happen, can't catch this earlier because we can't differentiate between # TypeError in parsing or TypeError within code (at runtime). except: self.report( Feedback("Something went wrong while parsing your code.")) return res
def parse_external(x): rep = Reporter.active_reporter res = (None, None) try: res = asttokens.ASTTokens(x, parse=True) return (res, res._tree) except IndentationError as e: e.filename = "script.py" # no line info for now rep.do_test( Test( Feedback( "Your code could not be parsed due to an error in the indentation:<br>`%s.`" % str(e)))) except SyntaxError as e: e.filename = "script.py" # no line info for now rep.do_test( Test( Feedback( "Your code can not be executed due to a syntax error:<br>`%s.`" % str(e)))) # Can happen, can't catch this earlier because we can't differentiate between # TypeError in parsing or TypeError within code (at runtime). except: rep.do_test( Test( Feedback("Something went wrong while parsing your code."))) return (res)
def check_function( name, index, missing_msg="FMT:Did you define the {typestr}?", params_not_matched_msg="FMT:Something went wrong in figuring out how you specified the " "arguments for `{name}`; have another look at your code and its output.", expand_msg=MSG_PREPEND, signature=True, typestr="{ordinal} function call `{name}()`", state=None): rep = Reporter.active_reporter stu_out = state.student_function_calls sol_out = state.solution_function_calls fmt_kwargs = {'ordinal': get_ord(index + 1), 'index': index, 'name': name} fmt_kwargs['typestr'] = typestr.format(**fmt_kwargs) # Get Parts ---- try: stu_parts = stu_out[name][index] except (KeyError, IndexError): _msg = state.build_message(missing_msg, fmt_kwargs) rep.do_test(Test(Feedback(_msg, state.highlight))) sol_parts = sol_out[name][index] # Signatures ----- if signature: signature = None if isinstance(signature, bool) else signature get_sig = partial(getSignatureInProcess, name=name, signature=signature, manual_sigs=state.get_manual_sigs()) try: sol_sig = get_sig(mapped_name=sol_parts['name'], process=state.solution_process) sol_parts['args'], _ = bind_args(sol_sig, sol_parts['args']) except: raise ValueError( "Something went wrong in matching call index {index} of {name} to its signature. " "You might have to manually specify or correct the signature.". format(index=index, name=name)) try: stu_sig = get_sig(mapped_name=stu_parts['name'], process=state.student_process) stu_parts['args'], _ = bind_args(stu_sig, stu_parts['args']) except Exception as e: _msg = state.build_message(params_not_matched_msg, fmt_kwargs) rep.do_test(Test(Feedback(_msg, stu_parts['node']))) # three types of parts: pos_args, keywords, args (e.g. these are bound to sig) append_message = {'msg': expand_msg, 'kwargs': fmt_kwargs} child = part_to_child(stu_parts, sol_parts, append_message, state, node_name='function_calls') return child
def with_context(state, *args, child=None): # set up context in processes solution_res = setUpNewEnvInProcess( process=state.solution_process, context=state.solution_parts["with_items"] ) if isinstance(solution_res, Exception): raise InstructorError( "error in the solution, running test_with(): %s" % str(solution_res) ) student_res = setUpNewEnvInProcess( process=state.student_process, context=state.student_parts["with_items"] ) if isinstance(student_res, AttributeError): state.report( Feedback( "In your `with` statement, you're not using a correct context manager.", child.highlight, # TODO ) ) if isinstance(student_res, (AssertionError, ValueError, TypeError)): state.report( Feedback( "In your `with` statement, the number of values in your context manager " "doesn't correspond to the number of variables you're trying to assign it to.", child.highlight, ) ) # run subtests try: multi(state, *args) finally: # exit context close_solution_context = breakDownNewEnvInProcess( process=state.solution_process ) if isinstance(close_solution_context, Exception): raise InstructorError( "error in the solution, closing the `with` fails with: %s" % close_solution_context ) close_student_context = breakDownNewEnvInProcess(process=state.student_process) if isinstance(close_student_context, Exception): state.report( Feedback( "Your `with` statement can not be closed off correctly, you're " + "not using the context manager correctly.", state, ) ) return state
def call( args, test='value', incorrect_msg=MSG_CALL_INCORRECT, error_msg=MSG_CALL_ERROR, # TODO kept for backwards compatibility in test_function_definition/lambda argstr='', func=None, state=None, **kwargs): rep = Reporter.active_reporter test_type = ('value', 'output', 'error') get_func = evalCalls[test] # Run for Solution -------------------------------------------------------- eval_sol, str_sol = run_call(args, state.solution_parts['node'], state.solution_process, get_func, **kwargs) if (test == 'error') ^ isinstance(str_sol, Exception): _msg = state.build_message( "FMT:Calling for arguments {args} resulted in an error (or not an error if testing for one). Error message: {type_err} {str_sol}", dict(args=args, type_err=type(str_sol), str_sol=str_sol)) raise ValueError(_msg) if isinstance(eval_sol, ReprFail): _msg = state.build_message( "FMT:Can't get the result of calling it for arguments {args}: {eval_sol.info}", dict(args=args, eval_sol=eval_sol)) raise ValueError(_msg) # Run for Submission ------------------------------------------------------ eval_stu, str_stu = run_call(args, state.student_parts['node'], state.student_process, get_func, **kwargs) fmt_kwargs = { 'part': argstr, 'argstr': argstr, 'str_sol': str_sol, 'str_stu': str_stu } # either error test and no error, or vice-versa stu_node = state.student_parts['node'] if (test == 'error') ^ isinstance(str_stu, Exception): _msg = state.build_message(error_msg, fmt_kwargs) rep.do_test(Test(Feedback(_msg, stu_node))) # incorrect result _msg = state.build_message(incorrect_msg, fmt_kwargs) rep.do_test(EqualTest(eval_sol, eval_stu, Feedback(_msg, stu_node), func)) return state
def has_equal_ast( incorrect_msg="FMT: Your code does not seem to match the solution.", code=None, exact=True, state=None): """Test whether abstract syntax trees match between the student and solution code. Args: incorrect_msg: message displayed when ASTs mismatch. code: optional code to use instead of the solution AST exact: whether the representations must match exactly. If false, the solution AST only needs to be contained within the student AST (similar to using test student typed). :Example: Student and Solution Code:: dict(a = 'value').keys() SCT:: # all pass Ex().has_equal_ast() Ex().has_equal_ast(code = "dict(a = 'value').keys()") Ex().has_equal_ast(code = "dict(a = 'value')", exact = False) """ rep = Reporter.active_reporter def parse_tree(n): # get contents of module.body if only 1 element crnt = n.body[0] if isinstance(n, ast.Module) and len( n.body) == 1 else n # remove Expr if it exists return ast.dump(crnt.value if isinstance(crnt, ast.Expr) else crnt) stu_rep = parse_tree(state.student_tree) sol_rep = parse_tree(state.solution_tree if not code else ast.parse(code)) _msg = state.build_message(incorrect_msg) if exact: rep.do_test( EqualTest(stu_rep, sol_rep, Feedback(_msg, state.highlight))) elif not sol_rep in stu_rep: rep.do_test(Test(Feedback(_msg, state.highlight))) return state
def has_part(state, name, msg, fmt_kwargs=None, index=None): d = { "sol_part": state.solution_parts, "stu_part": state.student_parts, **fmt_kwargs, } def verify(part, index): if index is not None: if isinstance(index, list): for ind in index: part = part[ind] else: part = part[index] if part is None: raise KeyError # Chceck if it's there in the solution _msg = state.build_message(msg, d) _err_msg = "SCT fails on solution: " + _msg try: verify(state.solution_parts[name], index) except (KeyError, IndexError): raise InstructorError(_err_msg) try: verify(state.student_parts[name], index) except (KeyError, IndexError): state.report(Feedback(_msg, state)) return state
def check_part_index(name, index, part_msg, missing_msg="FMT:Are you sure it is defined?", state=None, expand_msg=""): """Return child state with indexed name part as its ast tree""" rep = Reporter.active_reporter # create message ordinal = "" if isinstance(index, str) else get_ord(index + 1) fmt_kwargs = {'index': index, 'ordinal': ordinal} fmt_kwargs['part'] = part_msg.format(**fmt_kwargs) append_message = {'msg': expand_msg, 'kwargs': fmt_kwargs} # check there are enough parts for index stu_parts = state.student_parts[name] try: stu_parts[index] except (KeyError, IndexError): _msg = state.build_message(missing_msg, append_message['kwargs']) rep.do_test(Test(Feedback(_msg, state.highlight))) # get part at index stu_part = state.student_parts[name][index] sol_part = state.solution_parts[name][index] # return child state from part return part_to_child(stu_part, sol_part, append_message, state)
def has_equal_part_len(state, name, unequal_msg): """Verify that a part that is zoomed in on has equal length. Typically used in the context of ``check_function_def()`` Arguments: name (str): name of the part for which to check the length to the corresponding part in the solution. unequal_msg (str): Message in case the lengths do not match. state (State): state as passed by the SCT chain. Don't specify this explicitly. :Examples: Student and solution code:: def shout(word): return word + '!!!' SCT that checks number of arguments:: Ex().check_function_def('shout').has_equal_part_len('args', 'not enough args!') """ d = dict( stu_len=len(state.student_parts[name]), sol_len=len(state.solution_parts[name]) ) if d["stu_len"] != d["sol_len"]: _msg = state.build_message(unequal_msg, d) state.report(Feedback(_msg, state)) return state
def fail(msg="", state=None): """Fail test with message""" rep = Reporter.active_reporter _msg = state.build_message(msg) rep.do_test(Test(Feedback(_msg, state))) return state
def check_node( state, name, index=0, typestr="{{ordinal}} node", missing_msg=None, expand_msg=None ): if missing_msg is None: missing_msg = "系统想要检查 {{typestr}} 但没有找到它." if expand_msg is None: expand_msg = "检查 {{typestr}}. " stu_out = state.ast_dispatcher(name, state.student_ast) sol_out = state.ast_dispatcher(name, state.solution_ast) # check if there are enough nodes for index fmt_kwargs = { "ordinal": get_ord(index + 1) if isinstance(index, int) else "", "index": index, "name": name, } fmt_kwargs["typestr"] = render(typestr, fmt_kwargs) # test if node can be indexed succesfully try: stu_out[index] except (KeyError, IndexError): # TODO comment errors _msg = state.build_message(missing_msg, fmt_kwargs) state.report(Feedback(_msg, state)) # get node at index stu_part = stu_out[index] sol_part = sol_out[index] append_message = {"msg": expand_msg, "kwargs": fmt_kwargs} return part_to_child(stu_part, sol_part, append_message, state, node_name=name)
def do_test(self, testobj, prepend_on_fail="", fallback_ast=None): """Do test. Execute a given test, unless some previous test has failed. If the test has failed, the state of the reporter changes and the feedback is kept. """ if prepend_on_fail: self.failure_msg = prepend_on_fail if fallback_ast: self.fallback_ast = fallback_ast if isinstance(testobj, Test): testobj.test() result = testobj.result if (not result): self.failed_test = True self.feedback = testobj.get_feedback() self.feedback.message = self.failure_msg + self.feedback.message if not self.feedback.line_info and self.fallback_ast: self.feedback = Feedback(self.feedback.message, self.fallback_ast) raise TestFail else: result = None testobj() # run function for side effects #self.failure_msg_stack.pop() return result
def has_equal_key(key, incorrect_value_msg=MSG_INCORRECT_VAL, key_missing_msg=MSG_KEY_MISSING, name=None, state=None): rep = Reporter.active_reporter sol_name = name or state.solution_parts.get('name') stu_name = name or state.student_parts.get('name') has_key(key, key_missing_msg, state=state) sol_value, sol_str = getValueInProcess(sol_name, key, state.solution_process) if isinstance(sol_value, ReprFail): raise NameError( "Value from %r can't be fetched from the solution process: %s" % c(sol_name, sol_value.info)) # check if value ok _msg = state.build_message(incorrect_value_msg, {'key': key}) rep.do_test( EqualValueProcessTest(stu_name, key, state.student_process, sol_value, Feedback(_msg, state.highlight))) return state
def check_object(index, missing_msg=MSG_UNDEFINED, expand_msg=MSG_PREPEND, state=None, typestr="variable"): rep = Reporter.active_reporter if not isDefinedInProcess(index, state.solution_process): raise NameError("%r not in solution environment " % index) append_message = { 'msg': expand_msg, 'kwargs': { 'index': index, 'typestr': typestr } } # create child state, using either parser output, or create part from name fallback = lambda: ObjectAssignmentParser.get_part(index) stu_part = state.student_object_assignments.get(index, fallback()) sol_part = state.solution_object_assignments.get(index, fallback()) # test object exists _msg = state.build_message(missing_msg, append_message['kwargs']) rep.do_test( DefinedProcessTest(index, state.student_process, Feedback(_msg))) child = part_to_child(stu_part, sol_part, append_message, state) return child
def has_no_error( incorrect_msg="Have a look at the console: your code contains an error. Fix it and try again!", state=None): """Check whether the submission did not generate a runtime error. If all SCTs for an exercise pass, before marking the submission as correct pythonwhat will automatically check whether the student submission generated an error. This means it is not needed to use ``has_no_error()`` explicitly. However, in some cases, using ``has_no_error()`` explicitly somewhere throughout your SCT execution can be helpful: - If you want to make sure people didn't write typos when writing a long function name. - If you want to first verify whether a function actually runs, before checking whether the arguments were specified correctly. - More generally, if, because of the content, it's instrumental that the script runs without errors before doing any other verifications. Args: incorrect_msg: if specified, this overrides the default message if the student code generated an error. :Example: Suppose you're verifying an exercise about model training and validation: :: # pre exercise code import numpy as np from sklearn.model_selection import train_test_split from sklearn import datasets from sklearn import svm iris = datasets.load_iris() iris.data.shape, iris.target.shape # solution X_train, X_test, y_train, y_test = train_test_split( iris.data, iris.target, test_size=0.4, random_state=0) If you want to make sure that ``train_test_split()`` ran without errors, which would check if the student typed the function without typos and used sensical arguments, you could use the following SCT: :: Ex().has_no_error() Ex().check_function('sklearn.model_selection.train_test_split').multi( check_args(['arrays', 0]).has_equal_value(), check_args(['arrays', 0]).has_equal_value(), check_args(['options', 'test_size']).has_equal_value(), check_args(['options', 'random_state']).has_equal_value() ) If, on the other hand, you want to fall back onto pythonwhat's built in behavior, that checks for an error before marking the exercise as correct, you can simply leave of the ``has_no_error()`` step. """ state.assert_root('has_no_error') rep = Reporter.active_reporter if rep.error: _msg = state.build_message(incorrect_msg, {"error": str(rep.error)}) rep.do_test(Test(Feedback(_msg, state))) return state
def build_test(stud, sol, state, do_eval, eq_fun, feedback_msg, add_more, highlight=False, **kwargs): got_error = False if do_eval: eval_solution, str_solution = getResultInProcess( tree=sol, process=state.solution_process, **kwargs) if isinstance(str_solution, Exception): raise ValueError( "Running an argument in the solution environment raised an error" ) if isinstance(eval_solution, ReprFail): raise ValueError("Couldn't figure out the argument: " + eval_solution.info) eval_student, str_student = getResultInProcess( tree=stud, process=state.student_process, **kwargs) if isinstance(str_student, Exception): got_error = True # The (eval_student, ) part is important, because when eval_student is a tuple, we don't want # to expand them all over the %'s during formatting, we just want the tuple to be represented # in the place of the %r. Same for eval_solution. if add_more: if got_error: feedback_msg += " Expected `%s`, but got %s." % (str_solution, "an error") else: feedback_msg += " Expected `%s`, but got `%s`." % ( str_solution, str_student) else: # We don't want the 'expected...' message here. It's a pain in the ass to deparse the ASTs to # give something meaningful. eval_student = ast.dump(stud) eval_solution = ast.dump(sol) _msg = state.build_message(feedback_msg) return (Test(Feedback(_msg, stud if highlight else None)) if got_error else eq_fun(eval_student, eval_solution, Feedback(_msg, stud if highlight else None)))
def __init__(self): self.failed_test = False self.feedback = Feedback( "Oh no, your solution is incorrect! Please, try again.") self.success_msg = "Great work!" self.errors_allowed = False self.failure_msg = "" self.fallback_ast = None
def check_object(index, missing_msg=None, expand_msg=None, state=None, typestr="variable"): """Check object existence (and equality) Check whether an object is defined in the student's environment, and zoom in on its value in both student and solution environment to inspect quality (with has_equal_value(). Args: index (str): the name of the object which value has to be checked. missing_msg (str): feedback message when the object is not defined in the student's environment. expect_msg (str): prepending message to put in front. :Example: Student code:: b = 1 c = 3 Solution code:: a = 1 b = 2 c = 3 SCT:: Ex().check_object("a") # fail Ex().check_object("b") # pass Ex().check_object("b").has_equal_value() # fail Ex().check_object("c").has_equal_value() # pass """ if missing_msg is None: missing_msg = "__JINJA__:Did you define the {{typestr}} `{{index}}` without errors?" if expand_msg is None: expand_msg = "__JINJA__:Did you correctly define the {{typestr}} `{{index}}`? " rep = Reporter.active_reporter if not isDefinedInProcess(index, state.solution_process): raise NameError("%r not in solution environment " % index) append_message = {'msg': expand_msg, 'kwargs': {'index': index, 'typestr': typestr}} # create child state, using either parser output, or create part from name fallback = lambda: ObjectAssignmentParser.get_part(index) stu_part = state.student_object_assignments.get(index, fallback()) sol_part = state.solution_object_assignments.get(index, fallback()) # test object exists _msg = state.build_message(missing_msg, append_message['kwargs']) rep.do_test(DefinedProcessTest(index, state.student_process, Feedback(_msg))) child = part_to_child(stu_part, sol_part, append_message, state) return child
def with_context(*args, state=None): rep = Reporter.active_reporter # set up context in processes solution_res = setUpNewEnvInProcess( process=state.solution_process, context=state.solution_parts['with_items']) if isinstance(solution_res, Exception): raise Exception("error in the solution, running test_with(): %s" % str(solution_res)) student_res = setUpNewEnvInProcess( process=state.student_process, context=state.student_parts['with_items']) if isinstance(student_res, AttributeError): rep.do_test( Test( Feedback( "In your `with` statement, you're not using a correct context manager.", child.highlight))) if isinstance(student_res, (AssertionError, ValueError, TypeError)): rep.do_test( Test( Feedback( "In your `with` statement, the number of values in your context manager " "doesn't correspond to the number of variables you're trying to assign it to.", child.highlight))) # run subtests try: multi(*args, state=state) finally: # exit context if breakDownNewEnvInProcess(process=state.solution_process): raise Exception( "error in the solution, closing the `with` fails with: %s" % (close_solution_context)) if breakDownNewEnvInProcess(process=state.student_process): rep.do_test(Test(Feedback("Your `with` statement can not be closed off correctly, you're " + \ "not using the context manager correctly.", state))) return state
def has_equal_part_len(name, insufficient_msg, state=None): rep = Reporter.active_reporter d = dict(stu_len=len(state.student_parts[name]), sol_len=len(state.solution_parts[name])) if d['stu_len'] != d['sol_len']: _msg = state.build_message(insufficient_msg, d) rep.do_test(Test(Feedback(_msg, state.highlight))) return state
def has_equal_ast( incorrect_msg="FMT: Your code does not seem to match the solution.", state=None): rep = Reporter.active_reporter stu_rep = ast.dump(state.student_tree) sol_rep = ast.dump(state.solution_tree) _msg = state.build_message(incorrect_msg) rep.do_test(EqualTest(stu_rep, sol_rep, Feedback(_msg, state.highlight))) return state
def has_equal_part(state, name, msg): d = { "stu_part": state.student_parts, "sol_part": state.solution_parts, "name": name, } _msg = state.build_message(msg, d) state.do_test( EqualTest(d["stu_part"][name], d["sol_part"][name], Feedback(_msg, state)) ) return state
def has_equal_key(key, incorrect_value_msg=MSG_INCORRECT_VAL, key_missing_msg=MSG_KEY_MISSING, state=None): """Check whether an object (dict, DataFrame, etc) has a key, and whether this key is correct when comparing to the solution code. ``has_equal_key()`` can currently only be used when chained from ``check_object()``, the function that is used to 'zoom in' on the object of interest. Args: key (str): Name of the key that the object should have. incorrect_value_msg (str): When specified, this overrides the automatically generated message in case the key does not correspond to the value of the key in the solution process. key_missing_msg (str): When specified, this overrides the automatically generated message in case the key does not exist. state (State): The state that is passed in through the SCT chain (don't specify this). :Example: Student code and solution code:: x = {'a': 2} SCT:: # Verify that x contains a key a and whether it is correct Ex().check_object('x').has_equal_key('a') """ rep = Reporter.active_reporter sol_name = state.solution_parts.get('name') stu_name = state.student_parts.get('name') has_key(key, key_missing_msg, state=state) sol_value, sol_str = getValueInProcess(sol_name, key, state.solution_process) if isinstance(sol_value, ReprFail): raise NameError( "Value from %r can't be fetched from the solution process: %s" % c(sol_name, sol_value.info)) # check if value ok _msg = state.build_message(incorrect_value_msg, {'key': key}) rep.do_test( EqualValueProcessTest(stu_name, key, state.student_process, sol_value, Feedback(_msg, state))) return state
def has_equal_part(name, msg, state): rep = Reporter.active_reporter d = { 'stu_part': state.student_parts, 'sol_part': state.solution_parts, 'name': name } _msg = state.build_message(msg, d) rep.do_test( EqualTest(d['stu_part'][name], d['sol_part'][name], Feedback(_msg, state))) return state
def __init__(self, feedback): """ Initialize the standard test. Args: feedback: string or Feedback object """ if (issubclass(type(feedback), Feedback)): self.feedback = feedback elif (issubclass(type(feedback), str)): self.feedback = Feedback(feedback) else: raise TypeError("When creating a test, specify either a string or a Feedback object") self.result = None
def has_part(name, msg, state=None, fmt_kwargs=None): rep = Reporter.active_reporter d = { 'sol_part': state.solution_parts, 'stu_part': state.student_parts, **fmt_kwargs } try: part = state.student_parts[name] if part is None: raise KeyError except (KeyError, IndexError): _msg = state.build_message(msg, d) rep.do_test(Test(Feedback(_msg, state.highlight))) return state