Example #1
0
def has_printout(state,
                 index,
                 not_printed_msg=None,
                 pre_code=None,
                 name=None,
                 copy=False):
    """Check if the right printouts happened.

    ``has_printout()`` will look for the printout in the solution code that you specified with ``index`` (0 in this case), rerun the ``print()`` call in
    the solution process, capture its output, and verify whether the output is present in the output of the student.

    This is more robust as ``Ex().check_function('print')`` initiated chains as students can use as many
    printouts as they want, as long as they do the correct one somewhere.

    Args:
        index (int): index of the ``print()`` call in the solution whose output you want to search for in the student output.
        not_printed_msg (str): if specified, this overrides the default message that is generated when the output
          is not found in the student output.
        pre_code (str): Python code as a string that is executed before running the targeted student call.
          This is the ideal place to set a random seed, for example.
        copy (bool): whether to try to deep copy objects in the environment, such as lists, that could
          accidentally be mutated. Disabled by default, which speeds up SCTs.
        state (State): state as passed by the SCT chain. Don't specify this explicitly.

    :Example:

        Suppose you want somebody to print out 4: ::

            print(1, 2, 3, 4)

        The following SCT would check that: ::

            Ex().has_printout(0)

        All of the following SCTs would pass: ::

            print(1, 2, 3, 4)
            print('1 2 3 4')
            print(1, 2, '3 4')
            print("random"); print(1, 2, 3, 4)

    :Example:

        Watch out: ``has_printout()`` will effectively **rerun** the ``print()`` call in the solution process after the entire solution script was executed.
        If your solution script updates the value of `x` after executing it, ``has_printout()`` will not work.

        Suppose you have the following solution: ::

            x = 4
            print(x)
            x = 6

        The following SCT will not work: ::

            Ex().has_printout(0)

        Why? When the ``print(x)`` call is executed, the value of ``x`` will be 6, and pythonwhat will look for the output `'6`' in the output the student generated.
        In cases like these, ``has_printout()`` cannot be used.

    :Example:

        Inside a for loop ``has_printout()``

        Suppose you have the following solution: ::

            for i in range(5):
                print(i)

        The following SCT will not work: ::

            Ex().check_for_loop().check_body().has_printout(0)

        The reason is that ``has_printout()`` can only be called from the root state. ``Ex()``.
        If you want to check printouts done in e.g. a for loop, you have to use a `check_function('print')` chain instead: ::

            Ex().check_for_loop().check_body().\\
                set_context(0).check_function('print').\\
                check_args(0).has_equal_value()

    """

    extra_msg = "If you want to check printouts done in e.g. a for loop, you have to use a `check_function('print')` chain instead."
    state.assert_execution_root("has_printout", extra_msg=extra_msg)

    if not_printed_msg is None:
        not_printed_msg = (
            "Have you used `{{sol_call}}` to do the appropriate printouts?")

    try:
        sol_call_ast = state.ast_dispatcher.find(
            "function_calls", state.solution_ast)["print"][index]["node"]
    except (KeyError, IndexError):
        raise InstructorError.from_message(
            "`has_printout({})` couldn't find the {} print call in your solution."
            .format(index, get_ord(index + 1)))

    out_sol, str_sol = getOutputInProcess(
        tree=sol_call_ast,
        process=state.solution_process,
        context=state.solution_context,
        env=state.solution_env,
        pre_code=pre_code,
        copy=copy,
    )

    sol_call_str = state.solution_ast_tokens.get_text(sol_call_ast)

    if isinstance(str_sol, Exception):
        with debugger(state):
            state.report(
                "Evaluating the solution expression {} raised error in solution process."
                "Error: {} - {}".format(sol_call_str, type(out_sol), str_sol))

    has_output(
        state,
        out_sol.strip(),
        pattern=False,
        no_output_msg=FeedbackComponent(not_printed_msg,
                                        {"sol_call": sol_call_str}),
    )

    return state
Example #2
0
def has_printout(index,
                 not_printed_msg=None,
                 pre_code=None,
                 name=None,
                 copy=False,
                 state=None):
    """Check if the output of print() statement in the solution is in the output the student generated.

    This is more robust as ``Ex().check_function('print')`` initiated chains as students can use as many
    printouts as they want, as long as they do the correct one somewhere.

    .. note::

        When zooming in on parts of the student submission (with e.g. ``check_for_loop()``), we are not
        zooming in on the piece of the student output that is related to that piece of the student code.
        In other words, ``has_printout()`` always considers the entire student output.

    Args:
        index (int): index of the ``print()`` call in the solution whose output you want to search for in the student output.
        not_printed_msg (str): if specified, this overrides the default message that is generated when the output
          is not found in the student output.
        pre_code (str): Python code as a string that is executed before running the targeted student call.
          This is the ideal place to set a random seed, for example.
        copy (bool): whether to try to deep copy objects in the environment, such as lists, that could
          accidentally be mutated. Disabled by default, which speeds up SCTs.
        state (State): state as passed by the SCT chain. Don't specify this explicitly.

    :Example:

        Solution::

            print(1, 2, 3, 4)

        SCT::

            Ex().has_printout(0)

        Each of these submissions will pass::

            print(1, 2, 3, 4)
            print('1 2 3 4')
            print(1, 2, '3 4')
            print("random"); print(1, 2, 3, 4)
    """

    if not_printed_msg is None:
        not_printed_msg = "__JINJA__:Have you used `{{sol_call}}` to do the appropriate printouts?"

    try:
        sol_call_ast = state.solution_function_calls['print'][index]['node']
    except (KeyError, IndexError):
        raise ValueError(
            "Using has_printout() with index {} expects that there is/are at least {} print() call(s) in your solution."
            "Is that the case?".format(index, index + 1))

    out_sol, str_sol = getOutputInProcess(tree=sol_call_ast,
                                          process=state.solution_process,
                                          context=state.solution_context,
                                          env=state.solution_env,
                                          pre_code=pre_code,
                                          copy=copy)

    sol_call_str = state.solution_tree_tokens.get_text(sol_call_ast)

    if isinstance(str_sol, Exception):
        raise ValueError(
            "Evaluating the solution expression {} raised error in solution process."
            "Error: {} - {}".format(sol_call_str, type(out_sol), str_sol))

    _msg = state.build_message(not_printed_msg, {'sol_call': sol_call_str})

    has_output(out_sol.strip(), pattern=False, no_output_msg=_msg, state=state)

    return state