def test_args(arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg, child, quiet_child): if arg_names or arg_defaults: # test number of args has_equal_part_len('_spec1_args', nb_args_msg or MSG_NUM_ARGS, state=quiet_child) # iterate over each arg, testing name and default for ii in range(len(child.solution_parts['_spec1_args'])): # get argument state arg_state = check_part_index('_spec1_args', ii, 'argument', "NO MISSING MSG", state=child) # test exact name has_equal_part('name', arg_names_msg or MSG_BAD_ARG_NAME, arg_state) if arg_defaults: # test whether is default has_equal_part('is_default', arg_defaults_msg or MSG_BAD_DEFAULT, arg_state) # test default value, use if to prevent running a process no default if arg_state.solution_parts['is_default']: has_equal_value(arg_defaults_msg or MSG_INC_DEFAULT, "error message", state = arg_state) # test *args and **kwargs if child.solution_parts['*args']: vararg = check_part('*args', "", missing_msg = MSG_NO_VARARG, state = child) has_equal_part('name', MSG_VARARG_NAME, vararg) if child.solution_parts['**kwargs']: kwarg = check_part('**kwargs', "", missing_msg = MSG_NO_KWARGS, state = child) has_equal_part('name', MSG_KWARG_NAME, kwarg)
def test_object_after_expression(name, extra_env=None, context_vals=None, undefined_msg=None, incorrect_msg=None, expr_code=None, pre_code=None, state=None, **kwargs): """Test object after running an expression. Use ``has_equal_value()`` with the ``name`` argument instead. """ state.highlight = state.student_object_assignments.get(name, {}).get('highlight') has_equal_value(incorrect_msg=incorrect_msg, error_msg=undefined_msg, undefined_msg=undefined_msg, extra_env=extra_env, context_vals=context_vals, pre_code=pre_code, name=name, expr_code=expr_code, state=state, **kwargs)
def test_args(arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg, child, quiet_child): if arg_names or arg_defaults: # test number of args has_equal_part_len('_spec1_args', nb_args_msg or MSG_NUM_ARGS, state=quiet_child) # iterate over each arg, testing name and default for ii in range(len(child.solution_parts['_spec1_args'])): # get argument state arg_state = check_part_index('_spec1_args', ii, 'argument', "NO MISSING MSG", expand_msg="", state=child) # test exact name has_equal_part('name', arg_names_msg or MSG_BAD_ARG_NAME, arg_state) if arg_defaults: # test whether is default has_equal_part('is_default', arg_defaults_msg or MSG_BAD_DEFAULT, arg_state) # test default value, use if to prevent running a process no default if arg_state.solution_parts['is_default']: has_equal_value(incorrect_msg = arg_defaults_msg or MSG_INC_DEFAULT, error_msg="error message", append=True, state=arg_state) # test *args and **kwargs if child.solution_parts['*args']: vararg = check_part('*args', "", missing_msg=MSG_NO_VARARG, expand_msg="", state=child) has_equal_part('name', MSG_VARARG_NAME, state=vararg) if child.solution_parts['**kwargs']: kwarg = check_part('**kwargs', "", missing_msg=MSG_NO_KWARGS, expand_msg="", state=child) has_equal_part('name', MSG_KWARG_NAME, state=kwarg)
def test_object(name, eq_condition="equal", eq_fun=None, do_eval=True, undefined_msg=None, incorrect_msg=None, state=None): """Test object. The value of an object in the ending environment is compared in the student's environment and the solution environment. Args: name (str): the name of the object which value has to be checked. eq_condition (str): how objects are compared. Currently, only "equal" is supported, meaning that the object in student and solution process should have exactly the same value. do_eval (bool): if False, the object will only be checked for existence. Defaults to True. undefined_msg (str): feedback message when the object is not defined incorrect_msg (str): feedback message if the value of the object in the solution environment doesn't match the one in the student environment. :Example: Student code:: a = 1 b = 5 Solution code:: a = 1 b = 2 SCT:: test_object("a") # pass test_object("b") # fail Note that the student code below would fail both tests:: a = 1 b = 2 a = 3 # incorrect final value of a """ rep = Reporter.active_reporter rep.set_tag("fun", "test_object") child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg="", state=state) if do_eval: has_equal_value(incorrect_msg or MSG_INCORRECT, state=child)
def test_object(name, eq_condition="equal", eq_fun=None, do_eval=True, undefined_msg=None, incorrect_msg=None, state=None): """Test object. The value of an object in the ending environment is compared in the student's environment and the solution environment. Args: name (str): the name of the object which value has to be checked. eq_condition (str): how objects are compared. Currently, only "equal" is supported, meaning that the object in student and solution process should have exactly the same value. do_eval (bool): if False, the object will only be checked for existence. Defaults to True. undefined_msg (str): feedback message when the object is not defined incorrect_msg (str): feedback message if the value of the object in the solution environment doesn't match the one in the student environment. :Example: Student code:: a = 1 b = 5 Solution code:: a = 1 b = 2 SCT:: test_object("a") # pass test_object("b") # fail Note that the student code below would fail both tests:: a = 1 b = 2 a = 3 # incorrect final value of a """ rep = Reporter.active_reporter rep.set_tag("fun", "test_object") child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg = "", state=state) if do_eval: has_equal_value(incorrect_msg or MSG_INCORRECT, state=child)
def test_object(name, eq_condition="equal", eq_fun=None, do_eval=True, undefined_msg=None, incorrect_msg=None, state=None): child = check_object(name, undefined_msg or MSG_UNDEFINED, expand_msg="", state=state) if do_eval: has_equal_value(incorrect_msg or MSG_INCORRECT, state=child)
def arg_test(name, do_eval, missing_msg, incorrect_msg, state): arg_state = check_args(name=name, missing_msg=missing_msg, state=state) append = incorrect_msg is None if isinstance(do_eval, bool): if do_eval: has_equal_value(incorrect_msg=incorrect_msg, append=append, copy=False, state=arg_state) else: has_equal_ast(incorrect_msg=incorrect_msg, append=append, state=arg_state)
def test_expression_result(extra_env=None, context_vals=None, incorrect_msg=None, expr_code=None, pre_code=None, error_msg=None, state=None, **kwargs): """Test result of expression. Use the new ``has_equal_value()`` function instead. """ has_equal_value(incorrect_msg=incorrect_msg, error_msg=error_msg, extra_env=extra_env, context_vals=context_vals, expr_code=expr_code, pre_code=pre_code, state=state, **kwargs)
def test_expression_result(extra_env=None, context_vals=None, incorrect_msg=None, eq_condition="equal", expr_code=None, pre_code=None, keep_objs_in_env=None, error_msg=None, state=None): """Test result of expression. The code of the student is ran in the active state and the result of the evaluation is compared with the result of the solution. This can be used in nested pythonwhat calls like test_if_else. In these kind of calls, the code of the active state is set to the code in a part of the sub statement (e.g. the condition of an if statement). It has various parameters to control the execution of the (sub)expression. Args: extra_env (dict): set variables to the extra environment. They will update the student and solution environment in the active state before the student/solution code in the active state is ran. This argument should contain a dictionary with the keys the names of the variables you want to set, and the values are the values of these variables. context_vals (list): set variables which are bound in a for loop to certain values. This argument is only useful if you use the function in a test_for_loop. It contains a list with the values of the bound variables. incorrect_msg (str): feedback message if the result of the expression in the solution doesn't match the one of the student. This feedback message will be expanded if it is used in the context of another test function, like test_if_else. eq_condition (str): how results are compared. Currently, only "equal" is supported, meaning that the result in student and solution process should have exactly the same value. expr_code (str): if this variable is not None, the expression in the studeont/solution code will not be ran. Instead, the given piece of code will be ran in the student as well as the solution environment and the result will be compared. pre_code (str): the code in string form that should be executed before the expression is executed. This is the ideal place to set a random seed, for example. keep_obj_in_env (list()): a list of variable names that should be hold in the copied environment where the expression is evaluated. All primitive types are copied automatically, other objects have to be passed explicitely. error_msg (str): Message to override the default error message that is thrown if the expression resulted in an error. :Example: Student code:: a = 12 if a > 3: print('test %d' % a) Solution code:: a = 4 b = 5 if (a + 1) > (b - 1): print('test %d' % a) SCT:: test_if_else(1, test = test_expression_result( extra_env = { 'a': 3 } incorrect_msg = "Test if `a` > 3")) This SCT will pass as the condition in the student's code (:code:`a > 3`) will evaluate to the same value as the code in the solution code (:code:`(a + 1) > (b - 1)`), with value of :code:`a` set to :code:`3`. """ error_msg = error_msg or "Running an expression in the student process caused an error" if incorrect_msg is not None: feedback_msg = incorrect_msg else: # need to double bracket extra_env, so doesn't mess up str templating feedback_msg = ( "FMT:Unexpected expression: expected `{sol_eval}`, got `{stu_eval}` with values{extra_env}." ) has_equal_value(feedback_msg, error_msg, extra_env=extra_env, context_vals=context_vals, expr_code=expr_code, pre_code=pre_code, keep_objs_in_env=keep_objs_in_env, state=state)
def test_expression_result(extra_env=None, context_vals=None, incorrect_msg=None, eq_condition="equal", expr_code=None, pre_code=None, keep_objs_in_env=None, error_msg=None, state=None): """Test result of expression. The code of the student is ran in the active state and the result of the evaluation is compared with the result of the solution. This can be used in nested pythonwhat calls like test_if_else. In these kind of calls, the code of the active state is set to the code in a part of the sub statement (e.g. the condition of an if statement). It has various parameters to control the execution of the (sub)expression. Args: extra_env (dict): set variables to the extra environment. They will update the student and solution environment in the active state before the student/solution code in the active state is ran. This argument should contain a dictionary with the keys the names of the variables you want to set, and the values are the values of these variables. context_vals (list): set variables which are bound in a for loop to certain values. This argument is only useful if you use the function in a test_for_loop. It contains a list with the values of the bound variables. incorrect_msg (str): feedback message if the result of the expression in the solution doesn't match the one of the student. This feedback message will be expanded if it is used in the context of another test function, like test_if_else. eq_condition (str): how results are compared. Currently, only "equal" is supported, meaning that the result in student and solution process should have exactly the same value. expr_code (str): if this variable is not None, the expression in the studeont/solution code will not be ran. Instead, the given piece of code will be ran in the student as well as the solution environment and the result will be compared. pre_code (str): the code in string form that should be executed before the expression is executed. This is the ideal place to set a random seed, for example. keep_obj_in_env (list()): a list of variable names that should be hold in the copied environment where the expression is evaluated. All primitive types are copied automatically, other objects have to be passed explicitely. error_msg (str): Message to override the default error message that is thrown if the expression resulted in an error. :Example: Student code:: a = 12 if a > 3: print('test %d' % a) Solution code:: a = 4 b = 5 if (a + 1) > (b - 1): print('test %d' % a) SCT:: test_if_else(1, test = test_expression_result( extra_env = { 'a': 3 } incorrect_msg = "Test if `a` > 3")) This SCT will pass as the condition in the student's code (:code:`a > 3`) will evaluate to the same value as the code in the solution code (:code:`(a + 1) > (b - 1)`), with value of :code:`a` set to :code:`3`. """ error_msg = error_msg or "Running an expression in the student process caused an error" if incorrect_msg is not None: feedback_msg = incorrect_msg else: # need to double bracket extra_env, so doesn't mess up str templating feedback_msg = ( "FMT:Unexpected expression: expected `{sol_eval}`, got `{stu_eval}` with values{extra_env}." ) has_equal_value(feedback_msg, error_msg, extra_env = extra_env, context_vals=context_vals, expr_code=expr_code, pre_code=pre_code, keep_objs_in_env=keep_objs_in_env, state = state)
def test_object_after_expression(name, extra_env=None, context_vals=None, undefined_msg=None, incorrect_msg=None, eq_condition="equal", expr_code=None, pre_code=None, keep_objs_in_env=None, state=None): """Test object after expression. The code of the student is ran in the active state and the the value of the given object is compared with the value of that object in the solution. This can be used in nested pythonwhat calls like test_for_loop. In these kind of calls, the code of the active state is set to the code in a part of the sub statement (e.g. the body of a for loop). It has various parameters to control the execution of the (sub)expression. This test function is ideal to check if a value is updated correctly in the body of a for loop. Args: name (str): the name of the object which value has to be checked after evaluation of the expression. extra_env (dict): set variables to the extra environment. They will update the student and solution environment in the active state before the student/solution code in the active state is ran. This argument should contain a dictionary with the keys the names of the variables you want to set, and the values are the values of these variables. context_vals (list): set variables which are bound in a for loop to certain values. This argument is only useful if you use the function in a test_for_loop or test_function_definition. It contains a list with the values of the bound variables. incorrect_msg (str): feedback message if the value of the object in the solution environment doesn't match the one in the student environment. This feedback message will be expanded if it is used in the context of another test function, like test_for_loop. eq_condition (str): how objects are compared. Currently, only "equal" is supported, meaning that the resulting objects in student and solution process should have exactly the same value. expr_code (str): if this variable is not None, the expression in the studeont/solution code will not be ran. Instead, the given piece of code will be ran in the student as well as the solution environment and the result will be compared. pre_code (str): the code in string form that should be executed before the expression is executed. This is the ideal place to set a random seed, for example. keep_obj_in_env (list()): a list of variable names that should be hold in the copied environment where the expression is evaluated. All primitive types are copied automatically, other objects have to be passed explicitely. :Example: Student code:: count = 1 for i in range(100): count = count + i Solution code:: count = 15 for n in range(30): count = count + n SCT:: test_for_loop(1, body = test_object_after_expression("count", extra_env = { 'count': 20 }, contex_vals = [ 10 ]) This SCT will pass as the value of `count` is updated identically in the body of the for loop in the student code and solution code. """ if not undefined_msg: undefined_msg = "Have you defined `%s` without errors?" % name if not incorrect_msg: incorrect_msg = "Are you sure you assigned the correct value to `%s`?" % name ass_node = state.student_object_assignments.get(name, {}).get('highlight') has_equal_value( incorrect_msg = incorrect_msg, error_msg = undefined_msg, undefined_msg = undefined_msg, extra_env=extra_env, context_vals=context_vals, pre_code=pre_code, keep_objs_in_env=keep_objs_in_env, name = name, highlight = ass_node, expr_code = expr_code, state=state)
def test_object_after_expression(name, extra_env=None, context_vals=None, undefined_msg=None, incorrect_msg=None, eq_condition="equal", expr_code=None, pre_code=None, keep_objs_in_env=None, state=None, **kwargs): """Test object after expression. The code of the student is ran in the active state and the the value of the given object is compared with the value of that object in the solution. This can be used in nested pythonwhat calls like test_for_loop. In these kind of calls, the code of the active state is set to the code in a part of the sub statement (e.g. the body of a for loop). It has various parameters to control the execution of the (sub)expression. This test function is ideal to check if a value is updated correctly in the body of a for loop. Args: name (str): the name of the object which value has to be checked after evaluation of the expression. extra_env (dict): set variables to the extra environment. They will update the student and solution environment in the active state before the student/solution code in the active state is ran. This argument should contain a dictionary with the keys the names of the variables you want to set, and the values are the values of these variables. context_vals (list): set variables which are bound in a for loop to certain values. This argument is only useful if you use the function in a test_for_loop or test_function_definition. It contains a list with the values of the bound variables. incorrect_msg (str): feedback message if the value of the object in the solution environment doesn't match the one in the student environment. This feedback message will be expanded if it is used in the context of another test function, like test_for_loop. eq_condition (str): how objects are compared. Currently, only "equal" is supported, meaning that the resulting objects in student and solution process should have exactly the same value. expr_code (str): if this variable is not None, the expression in the studeont/solution code will not be ran. Instead, the given piece of code will be ran in the student as well as the solution environment and the result will be compared. pre_code (str): the code in string form that should be executed before the expression is executed. This is the ideal place to set a random seed, for example. keep_obj_in_env (list()): a list of variable names that should be hold in the copied environment where the expression is evaluated. All primitive types are copied automatically, other objects have to be passed explicitely. kwargs: named arguments which are the same as those used by ``has_equal_value``. :Example: Student code:: count = 1 for i in range(100): count = count + i Solution code:: count = 15 for n in range(30): count = count + n SCT:: test_for_loop(1, body = test_object_after_expression("count", extra_env = { 'count': 20 }, contex_vals = [ 10 ]) This SCT will pass as the value of `count` is updated identically in the body of the for loop in the student code and solution code. """ if not undefined_msg: undefined_msg = "Have you defined `%s` without errors?" % name if not incorrect_msg: incorrect_msg = "Are you sure you assigned the correct value to `%s`?" % name ass_node = state.student_object_assignments.get(name, {}).get('highlight') has_equal_value( incorrect_msg = incorrect_msg, error_msg = undefined_msg, undefined_msg = undefined_msg, extra_env=extra_env, context_vals=context_vals, pre_code=pre_code, keep_objs_in_env=keep_objs_in_env, name = name, highlight = ass_node, expr_code = expr_code, state=state, **kwargs)