def test_comp(typestr, comptype, index, iter_vars_names, not_called_msg, insufficient_ifs_msg, incorrect_iter_vars_msg, comp_iter, ifs, key=None, body=None, value=None, rep=None, state=None): MSG_INCORRECT_ITER_VARS = "Have you used the correct iterator variables?" MSG_INCORRECT_NUM_ITER_VARS = "Have you used {{num_vars}} iterator variables?" MSG_INSUFFICIENT_IFS = "Have you used {{sol_len}} ifs?" # make sure other messages are set to default if None if insufficient_ifs_msg is None: insufficient_ifs_msg = MSG_INSUFFICIENT_IFS # get comprehension child = check_node(comptype, index-1, typestr, missing_msg=not_called_msg, state=state) # test comprehension iter and its variable names (or number of variables) if comp_iter: multi(comp_iter, state=check_part("iter", "iterable part", state=child)) # test iterator variables default_msg = MSG_INCORRECT_ITER_VARS if iter_vars_names else MSG_INCORRECT_NUM_ITER_VARS has_context(incorrect_iter_vars_msg or default_msg, iter_vars_names, state=child) # test the main expressions. if body: multi(body, state=check_part("body", "body", state=child)) # list and gen comp if key: multi(key, state=check_part("key", "key part", state=child)) # dict comp if value: multi(value, state=check_part("value", "value part", state=child)) # "" # test a list of ifs. each entry corresponds to a filter in the comprehension. for i, if_test in enumerate(ifs or []): # test that ifs are same length has_equal_part_len('ifs', insufficient_ifs_msg, state=child) # test individual ifs multi(if_test, state=check_part_index("ifs", i, utils.get_ord(i+1) + " if", state=child))
def test_args(arg_names, arg_defaults, nb_args_msg, arg_names_msg, arg_defaults_msg, child, quiet_child): MSG_NUM_ARGS = "FMT:You should define {parent[typestr]} with {sol_len} arguments, instead got {stu_len}." MSG_BAD_ARG_NAME = "FMT:The {parent[ordinal]} {parent[part]} should be called `{sol_part[name]}`, instead got `{stu_part[name]}`." MSG_BAD_DEFAULT = "FMT:The {parent[part]} `{stu_part[name]}` should have no default." MSG_INC_DEFAULT = "FMT:The {parent[part]} `{stu_part[name]}` does not have the correct default." MSG_NO_VARARG = "FMT:Have you specified an argument to take a `*` argument and named it `{sol_part[*args][name]}`?" MSG_NO_KWARGS = "FMT:Have you specified an argument to take a `**` argument and named it `{sol_part[**kwargs][name]}`?" MSG_VARARG_NAME = "FMT:Have you specified an argument to take a `*` argument and named it `{sol_part[name]}`?" MSG_KWARG_NAME = "FMT:Have you specified an argument to take a `**` argument and named it `{sol_part[name]}`?" 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_with(index, context_vals=False, # whether to check number of context vals context_tests=None, # check on context expressions body=None, undefined_msg=None, context_vals_len_msg=None, context_vals_msg=None, state=None): """Test a with statement. with open_file('...') as bla: [ open_file('...').__enter__() ] with open_file('...') as file: [ ] """ MSG_NUM_CTXT = "Make sure to use the correct number of context variables. It seems you defined too many." MSG_NUM_CTXT2 = "Make sure to use the correct number of context variables. It seems you defined too little." MSG_CTXT_NAMES = "Make sure to use the correct context variable names. Was expecting `{{sol_vars}}` but got `{{stu_vars}}`." check_with = partial(check_node, 'withs', index-1, "{{ordinal}} `with` statement", state=state) child = check_with() child2 = check_with() if context_vals: # test context var names ---- has_context(incorrect_msg=context_vals_msg or MSG_CTXT_NAMES, exact_names = True, state=child) # test num context vars ---- has_equal_part_len('context', MSG_NUM_CTXT, state=child) # Context sub tests ---- if context_tests and not isinstance(context_tests, list): context_tests = [context_tests] for i, context_test in enumerate(context_tests or []): # partial the substate check, because the function uses two prepended messages check_context = partial(check_part_index, 'context', i, "%s context"%utils.get_ord(i+1), missing_msg=MSG_NUM_CTXT2) check_context(state=child) # test exist ctxt_state = check_context(state=child2) # sub tests multi(context_test, state=ctxt_state) # Body sub tests ---- if body is not None: body_state = check_part('body', 'body', state=child2) with_context(body, state=body_state)
def test_comp(typestr, comptype, index, iter_vars_names, not_called_msg, insufficient_ifs_msg, incorrect_iter_vars_msg, comp_iter, ifs, key=None, body=None, value=None, expand_message=True, rep=None, state=None): MSG_NOT_CALLED = "FMT:The system wants to check the {typestr} but hasn't found it." MSG_PREPEND = "FMT:Check the {typestr}. " MSG_INCORRECT_ITER_VARS = "FMT:Have you used the correct iterator variables in the {parent[typestr]}? Be sure to use the correct names." MSG_INCORRECT_NUM_ITER_VARS = "FMT:Have you used {num_vars} iterator variables in the {parent[typestr]}?" MSG_INSUFFICIENT_IFS = "FMT:Have you used {sol_len} ifs inside the {parent[typestr]}?" # if true, set expand_message to default (for backwards compatibility) expand_message = MSG_PREPEND if expand_message is True else (expand_message or "") # make sure other messages are set to default if None if insufficient_ifs_msg is None: insufficient_ifs_msg = MSG_INSUFFICIENT_IFS if not_called_msg is None: not_called_msg = MSG_NOT_CALLED # TODO MSG: function was not consistent with prepending, so use state w/o expand_message quiet_state = check_node(comptype, index - 1, typestr, not_called_msg, expand_msg="", state=state) # get comprehension state = check_node(comptype, index - 1, typestr, not_called_msg, expand_msg=None if expand_message else "", state=state) # test comprehension iter and its variable names (or number of variables) if comp_iter: multi(comp_iter, state=check_part("iter", "iterable part", state=state)) # test iterator variables default_msg = MSG_INCORRECT_ITER_VARS if iter_vars_names else MSG_INCORRECT_NUM_ITER_VARS has_context(incorrect_iter_vars_msg or default_msg, iter_vars_names, state=quiet_state) # test the main expressions. if body: multi(body, state=check_part("body", "body", expand_msg=None if expand_message else "", state=state)) # list and gen comp if key: multi(key, state=check_part("key", "key part", expand_msg=None if expand_message else "", state=state)) # dict comp if value: multi(value, state=check_part("value", "value part", expand_msg=None if expand_message else "", state=state)) # "" # test a list of ifs. each entry corresponds to a filter in the comprehension. for i, if_test in enumerate(ifs or []): # test that ifs are same length has_equal_part_len('ifs', insufficient_ifs_msg, state=quiet_state) # test individual ifs multi(if_test, state=check_part_index("ifs", i, utils.get_ord(i + 1) + " if", state=state))