Electives

Success message

When all tests in an SCT pass, pythonwhat will automatically generate a congratulatory message to present to the student. If you want to override this ‘success message’, you can use the success_msg() function.

Ex().check_object("x").has_equal_value()
success_msg("You are a hero when it comes to variable assignment!")

This article on the authoring docs describes how to write good success messages.

Multiple choice exercises

Multiple choice exercises are straightforward to test. Use has_chosen() to provide tailored feedback for both the incorrect options, as the correct option. Below is the markdown source for a multiple choice exercise example, with an SCT that uses has_chosen:

## The author of Python

```yaml
type: MultipleChoiceExercise
```

Who is the author of the Python programming language?

`@instructions`

- Roy Co
- Ronald McDonald
- Guido van Rossum

`@sct`

```{python}
Ex().has_chosen(correct = 3,
                msgs = ["That's someone who makes soups.",
                        "That's a clown who likes burgers.",
                        "Correct! Head over to the next exercise!"])
```
  • correct specifies the number of the correct answer in this list (1-base indexed).
  • msgs argument should be a list of strings with a length equal to the number of options. We encourage you to provide feedback messages that are informative and tailored to the (incorrect) option that people selected.

Notice that there’s no need for success_msg() in multiple choice exercises, as you have to specify the success message inside has_chosen(), along with the feedback for incorrect options.

Capabilities of multi

multi() is always used to ‘branch’ different chains of SCT functions, so that the same state is passed to two sub-chains. There are different ways

Comma separated arguments

Most commonly, multi() is used to convert this code

Ex().check_if_exp().check_body().has_equal_value()
Ex().check_if_exp().check_test().has_equal_value()

into this equivalent (and more performant) SCT:

Ex().check_if_exp().multi(
        check_body().has_equal_value(),
        check_test().has_equal_value()
        )

List or generator of subtests

Rather than one or more subtest args, multi can take a single list or generator of subtests. For example, the code below checks that the body of a list comprehension has equal value for 10 possible values of the iterator variable, i.

Ex().check_list_comp()
    .check_body()
    .multi(set_context(i=x).has_equal_value() for x in range(10))

Chaining off multi

Multi returns the same state, or focus, it was given, so whatever comes after multi will run the same as if multi wasn’t used. For example, the code below tests a list comprehension’s body, followed by its iterator.

Ex().check_list_comp() \
    .multi(check_body().has_equal_value()) \
    .check_iter().has_equal_value()

has_context

Tests whether context variables defined by the student match the solution, for a selected block of code. A context variable is one that is defined in a looping or block statement. For example, ii in the code below.

[ii + 1 for ii in range(3)]

By default, the test fails if the submission code does not have the same number of context variables. This is illustrated below.

# solution
# ii and ltr are context variables
for ii, ltr in enumerate(['a']): pass

# sct
Ex().check_for_loop().check_body().has_context()

# passing submission
# still 2 variables, just different names
for jj, Ltr in enumerate(['a']): pass

# failing submission
# only 1 variable
for ii in enumerate(['a']): pass

Note

If you use has_context(exact_names = True), then the submission must use the same names for the context variables, which would cause the passing submission above to fail.

set_context

Sets the value of a temporary variable, such as ii in the list comprehension below.

[ii + 1 for ii in range(3)]

Variable names may be specified using positional or keyword arguments.

Example

# solution
ltrs = ['a', 'b']
for ii, ltr in enumerate(ltrs):
    print(ii)

# sct
Ex().check_for_loop().check_body() \
    .set_context(ii=0, ltr='a').has_equal_output() \
    .set_context(ii=1, ltr='b').has_equal_output()

Note that if a student replaced ii with jj in their submission, set_context would still work. It uses the solution code as a reference. While we specified the target variables ii and ltr by name in the SCT above, they may also be given by position..

Ex().check_for_loop().check_body().set_context(0, 'a').has_equal_output()

with_context

with_context(state, *args, child=None)

Runs subtests after setting the context for a with statement.

This function takes arguments in the same form as multi.

Context Managers Explained

With statements are special in python in that they enter objects called a context manager at the beginning of the block, and exit them at the end. For example, the object returned by open('fname.txt') below is a context manager.

with open('fname.txt') as f:
    print(f.read())

This code runs by

  1. assigning f to the context manager returned by open('fname.txt')
  2. calling f.__enter__()
  3. running the block
  4. calling f.__exit__()

with_context was designed to emulate this sequence of events, by setting up context values as in step (1), and replacing step (3) with any sub-tests given as arguments.