Part A
Contents
Part A#
A1#
import math
class Rectangle():
def __init__(self, side):
self.height = 2 * side
self.width = 3 * side
self.area = self.height * self.width
self.angles = [90., 90., 90., 90.]
self.diag1, self.diag2 = 2 * [math.sqrt(self.height ** 2
+ self.width ** 2)]
def to_parallelogram(self, ang):
self.angles = [ang, 180. - ang, ang, 180. - ang]
self.diag1 = math.sqrt(self.height ** 2 + self.width ** 2
+ 2 * self.height * self.width * math.cos(ang))
self.diag2 = math.sqrt(self.height ** 2 + self.width ** 2
- 2 * self.height * self.width * math.cos(ang))
def __str__(self):
return f'Quadrilateral with angles = {self.angles}'
In the code snippet above, which answer uses the correct terminology for the different components?
Answer 1:
module(s):
math
,self
class(es):
Rectangle
function(s):
sqrt
,cos
method(s):
__init__()
,__str__()
,diag1
,diag2
,angles
attribute(s):
height
,width
,area
Answer 2:
module(s):
math
class(es):
Rectangle
function(s):
sqrt
,cos
,area
,to_parallelogram()
method(s):
__init__()
,to_parallelogram()
,__str__()
attribute(s):
height
,width
,area
,angles
,diag1
,diag2
Answer 3:
module(s):
math
class(es):
Rectangle
function(s):
sqrt
,cos
method(s):
__init__()
,to_parallelogram()
,__str__()
attribute(s):
height
,width
,area
,angles
,diag1
,diag2
Answer 4:
module(s):
math
class(es):
Rectangle
function(s):
sqrt
,cos
,to_parallelogram()
method(s):
__init__()
,__str__()
attribute(s):
height
,width
,area
,angles
,diag1
,diag2
,self
Correct answer
Answer 3
A2#
import random as rd
import matplotlib.pyplot as plt
from math import sqrt
from turtle import Turtle
Given the code snippet above, are the following statements true or false?
To score a point on this exercise you need to get all 4 right.
sqrt
andTurtle
are a function and a class from themath
andturtle
modules: T/Fsqrt
andTurtle
are functions in themath
andturtle
modules: T/Frd
andplt
are functions in therandom
andmatplotlib.pyplot
modules: T/Frd
andplt
are import aliases for therandom
andmatplotlib.pyplot
modules: T/F
Correct answers
1: true. 2: false. 3: false. 4: true.
A3#
Write a function number_of_occurrences(interesting_words, text)
that checks how many times the words in the list interesting_words
appear in the string text. The function shall return a dictionary with the words as keys and the number of occurrences as values.
Hint: The call re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
returns a list of all the words in the string text.
See the example code and expected printout below.
# Example code
seasons = ['spring', 'summer', 'autumn', 'winter']
text = """The four seasons are spring, summer, autumn, and winter,
and although various areas of the United States experience drastically
different weather during these times, all portions of the country
recognize the seasons; winter in California may bring heat, and winter
in New York may bring blizzards, but both periods are nevertheless winter."""
word_to_count = number_of_occurrences(seasons, text)
print('The following seasons were mentioned in the text:')
for word, number in word_to_count.items():
print(f'\t{word} was mentioned {number} time(s)')
# The example code should print the following:
The following seasons were mentioned in the text:
spring was mentioned 1 time(s)
summer was mentioned 1 time(s)
autumn was mentioned 1 time(s)
winter was mentioned 4 time(s)
Want to check whether your solution is correct? Save it as A3.py. Then create another file in the same folder, copy-paste the code below into it, save it as A3_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A3']
function_name = 'number_of_occurrences'
correct_output_type = 'dictionary'
module_to_import = 're'
arg_names = ['interesting_words', 'text']
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
seasons = ['spring', 'summer', 'autumn', 'winter']
text = """The four seasons are spring, summer, autumn, and winter,
and although various areas of the United States experience drastically
different weather during these times, all portions of the country
recognize the seasons; winter in California may bring heat, and winter
in New York may bring blizzards, but both periods are nevertheless winter."""
visible_inputs = [[seasons, text]]
visible_correct_output = [{'spring': 1, 'summer': 1, 'autumn': 1, 'winter': 4}]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
interesting_words = ['how', 'you']
text = 'Hi, how are you? Are you okay?'
hidden_inputs = [[interesting_words, text]]
hidden_correct_output = [{'how': 1, 'you': 2}]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solution
import re
def number_of_occurrences(interesting_words, text):
word2count = {}
words = re.findall(r'[a-zA-ZåäöÅÄÖ]+', text)
for word in words:
word = word.lower()
if word in interesting_words:
if word in word2count:
word2count[word] += 1
else:
word2count[word] = 1
return word2count
Common mistakes
print
instead ofreturn
Counts all words, not just the ones in
interesting_words
.
A4#
Write a function bounded_squares(n)
that takes an integer \( n > 0\) as input and returns a list of all positive integers \(p\) that satisfy \(n^2 < p^2 < 3n^2\).
See the example code and expected printout below.
# Example code
print(bounded_squares(1))
print(bounded_squares(2))
print(bounded_squares(5))
# The example code should print the following:
[]
[3]
[6, 7, 8]
Want to check whether your solution is correct? Save it as A4.py. Then create another file in the same folder, copy-paste the code below into it, save it as A4_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A4']
function_name = 'bounded_squares'
correct_output_type = 'list'
module_to_import = 'math'
arg_names = ['n']
#-----------------------------------
def bounded_squares_correct(n):
outlist = []
p = n + 1
while p**2 < 3*n**2:
outlist.append(p)
p += 1
return outlist
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[1], [2], [5]]
visible_correct_output = [[], [3], [6, 7, 8]]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[101], [1001]]
hidden_correct_output = [bounded_squares_correct(*n) for n in hidden_inputs]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solution
# Alternative 1: using a while-loop
def bounded_squares(n):
outlist = []
p = n + 1
while p**2 < 3*n**2:
outlist.append(p)
p += 1
return outlist
# Alternative 2: first determine the smallest and largest possible values of p
def bounded_squares(n):
p_min = n+1
p_max = int(3**0.5 * n)
return [p for p in range(p_min, p_max + 1)]
Common mistakes
print
instead ofreturn
Hard-coded upper limit for
p
, e.g.p=10
orp=100
A5#
Consider the code below:
class Animal:
"""Representation of an animal.
self.species: string specifying what animal.
Examples: 'cat', 'dog', 'elephant'
self.position: tuple of x- and y-coordinates
Examples: (0, 0), (1, 0), (-4, 9)
"""
def __init__(self, species='cat', position=(0,0)):
self.species = species
self.position = position
def move_to(self, new_position):
self.position = new_position
dog = Animal('dog', (0, 1))
cat = Animal('cat', (3, 4))
print(dog)
print(cat)
cat.move_to((-1, 4))
print(cat)
When this code is executed, it produces the cryptic output in Printout 1:
# Printout 1 (caused by the example code above)
<__main__.Animal object at 0x7f810813caf0>
<__main__.Animal object at 0x7f8108131be0>
<__main__.Animal object at 0x7f8108131be0>
Add a method to the class Animal
such that the same print-statements produce the desired output in Printout 2:
# Printout 2 (desired printout)
dog at position (0, 1)
cat at position (3, 4)
cat at position (-1, 4)
Note that you should only add one method to the class. You are NOT allowed to change anything else in the code.
Example solution
def __str__(self):
return f'{self.species} at position {self.position}'
A6#
Write a function min_max_list()
that takes a list of integers as input and returns a tuple containing the list’s minimum and maximum values. You are NOT allowed to use any of the following:
the built-in functions
sorted()
,min()
andmax()
,the list method
sort()
or the functionsorted()
classes or functions from the
collections
ornumpy
modules.
The following statements:
# Example code
print(min_max_list([7, -90, 8, 34, 7, -56, 89]))
print(min_max_list([21, 17, 27, 4, 11, 15, 8, 13]))
should produce the printout
(-90, 89)
(4, 27)
Hint: You may use the following algorithm to compute the list’s maximum value:
Assign the first value in the list to a variable, which we here call x_max.
For the next value, x, in the list, if x is greater than x_max, then set x_max = x.
Repeat step 2 for all remaining values in the list.
Once step 3 has been completed, the list’s maximum value should be stored in the variable x_max. The minimum value can be computed in similar fashion.
Want to check whether your solution is correct? Save it as A6.py. Then create another file in the same folder, copy-paste the code below into it, save it as A6_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A6']
function_name = 'min_max_list'
correct_output_type = 'tuple'
module_to_import = 'math'
arg_names = ['lst']
forbidden_functions = ['sort', 'sorted', 'min', 'max']
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[[7, -90, 8, 34, 7, -56, 89]], [[21, 17, 27, 4, 11, 15, 8, 13]]]
visible_correct_output = [(-90, 89), (4, 27)]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[[22, 17, 21, -3, -1, 15, -2, 13]]]
hidden_correct_output = [(-3, 22)]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solution
def min_max_list(inlist):
vmin = inlist[0]
vmax = inlist[0]
for val in inlist:
if val < vmin:
vmin = val
if val > vmax:
vmax = val
return vmin, vmax
Common mistakes
Returns the wrong data type, e.g. a string or a list instead of a tuple
print
instead ofreturn
A7#
Consider the Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,…, where every number in the sequence (except the first two) equals the sum of the two preceding numbers. Write a function fibonacci_numbers(limit)
that returns a list of all the Fibonacci numbers that are less than or equal to limit
.
The following code:
print(fibonacci_numbers(4))
print(fibonacci_numbers(13))
should produce the printout
[0, 1, 1, 2, 3]
[0, 1, 1, 2, 3, 5, 8, 13]
Note that your function must work for arbitrarily large values of the limit
parameter.
Want to check whether your solution is correct? Save it as A7.py. Then create another file in the same folder, copy-paste the code below into it, save it as A7_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A7']
function_name = 'fibonacci_numbers'
correct_output_type = 'list'
module_to_import = 'math'
arg_names = ['limit']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[4], [13]]
visible_correct_output = [[0, 1, 1, 2, 3], [0, 1, 1, 2, 3, 5, 8, 13]]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [[1], [2], [3], [30], [1001]]
hidden_correct_output = [[0, 1, 1], [0, 1, 1, 2], [0, 1, 1, 2, 3],
[0, 1, 1, 2, 3, 5, 8, 13, 21],
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solution
def fibonacci_numbers(limit):
numbers = [0]
next_number = 1
while next_number <= limit:
numbers.append(next_number)
next_number = numbers[-2] + numbers[-1]
return numbers
Common mistakes
Hard-coded solutions that only work for small values of
limit
, e.g.limit
< 100Implicitly assumes that the return list will contain at most
limit
numbers, which is not true iflimit
<= 3print
instead ofreturn
A8#
Write a function sorted_dictionary(input_dict)
that takes a dictionary whose values are lists of integers as parameter and returns another dictionary in which the lists are sorted. The output dictionary should have the same keys as the input. The input dictionary should remain unchanged after the function call.
The following code:
d = {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
d_sorted = sorted_dictionary(d)
print('Original: ', d)
print('Sorted: ', d_sorted)
should produce the printout
Original: {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
Sorted: {'a1': [3, 17, 21, 22], 'a2': [8, 11, 13, 15], 'a3': [2, 7, 11, 13]}
Note that your function should work for dictionaries with other keys and lists than in the example.
Want to check whether your solution is correct? Save it as A8.py. Then create another file in the same folder, copy-paste the code below into it, save it as A8_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A8']
function_name = 'sorted_dictionary'
correct_output_type = 'dictionary'
module_to_import = 'math'
arg_names = ['d']
forbidden_functions = []
err_msg_mod = 'modifies_input_dict_with_lists'
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
d = {'a1': [21, 17, 22, 3], 'a2': [11, 15, 8, 13], 'a3': [7, 13, 2, 11]}
d_sorted = {'a1': [3, 17, 21, 22], 'a2': [8, 11, 13, 15], 'a3': [2, 7, 11, 13]}
visible_inputs = [[d]]
visible_correct_output = [d_sorted]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
d2 = {'b1': [7, -90, 8], 'b2': [ 34, 7, -56, 89]}
d2_sorted = {'b1': [-90, 7, 8], 'b2': [-56, 7, 34, 89]}
hidden_inputs = [[d2]]
hidden_correct_output = [d2_sorted]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions, error_msg_if_modifies_input=err_msg_mod)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solution
def sorted_dictionary(input_dict):
sorted_dict = {}
for key, value in input_dict.items():
sorted_dict[key] = sorted(value)
return sorted_dict
Common mistakes
Incorrect use of global variables
Using the list method
sort()
to sort the existing lists in place (as opposed to creating new lists) and therefore sorting the original dictionary too.print
instead ofreturn
A9#
Write a function alternative_splicing(seq)
that ”splices” an input list seq
and returns a tuple of 3 lists such that every third element in `seq is written in the same output list. That is:
The 1st, 4th, 7th, etc. elements of
seq
are written in the first output list,The 2nd, 5th, 8th, etc. elements of
seq
are written in the second output list,The 3rd, 6th, 9th, etc. elements of
seq
are written in the third output list.
The following code:
u = [10, 9, -2, 16, 18, -13, 12, 18, -14]
v = [-14, -20, -12, 3, -20, 7, -6, 13]
print(alternative_splicing(u))
print(alternative_splicing(v))
should produce the printout
([10, 16, 12], [9, 18, 18], [-2, -13, -14])
([-14, 3, -6], [-20, -20, 13], [-12, 7])
Note that the three output lists will be of different lengths if the number of elements in seq
is not a multiple of 3 (see the example with the list v
above).
Want to check whether your solution is correct? Save it as A9.py. Then create another file in the same folder, copy-paste the code below into it, save it as A9_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A9']
function_name = 'alternative_splicing'
correct_output_type = 'tuple'
module_to_import = 'math'
arg_names = ['u']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [[[10, 9, -2, 16, 18, -13, 12, 18, -14]], [[-14, -20, -12, 3, -20, 7, -6, 13]]]
visible_correct_output = [([10, 16, 12], [9, 18, 18], [-2, -13, -14]), ([-14, 3, -6], [-20, -20, 13], [-12, 7])]
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
w = list(range(11, 11+13))
hidden_inputs = [[w]]
hidden_correct_output = [([11, 14, 17, 20, 23], [12, 15, 18, 21], [13, 16, 19, 22])]
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solutions
# Alternative 1: using slicing
def alternative_splicing(seq):
lst1 = seq[::3]
lst2 = seq[1::3]
lst3 = seq[2::3]
return lst1, lst2, lst3
# Alternative 2: using a for loop and the modulo operator
def alternative_splicing(seq):
lst1 = []
lst2 = []
lst3 = []
for i in range(len(seq)):
if i % 3 == 0:
lst1.append(seq[i])
elif i % 3 == 1:
lst2.append(seq[i])
else:
lst3.append(seq[i])
return lst1, lst2, lst3
Common mistakes
Hard-coded solutions that only work for lists with 8 or 9 elements
Returning the wrong data type, e.g. a string or a list instead of a tuple.
print
instead ofreturn
A10#
Write a function dna_to_rna(dna)
that transcribes a string representing a DNA sequence to RNA and returns the RNA string. You can assume that the DNA string only contains the characters ’A’, ’C’, ’G’ and ’T’. For simplicity, assume that the Thymine (T) base is always transcribed to Uracil (U) while the other bases (A, C, G) remain unchanged.
The following code:
dna = 'GTCCCTAGGGGACTCACAATTGAAGTGGCA'
rna = dna_to_rna(dna)
print('DNA: ', dna)
print('RNA: ', rna)
should produce the printout
DNA: GTCCCTAGGGGACTCACAATTGAAGTGGCA
RNA: GUCCCUAGGGGACUCACAAUUGAAGUGGCA
Want to check whether your solution is correct? Save it as A10.py. Then create another file in the same folder, copy-paste the code below into it, save it as A10_test.py and run it.
import importlib
import copy
import re
import multiprocessing
def read_py_as_text(filename):
"""Return file contents as str"""
with open(filename, 'r') as pyfile:
lines_of_text = pyfile.readlines()
return lines_of_text
def remove_comments(lines_of_text):
return [re.sub(r'#.*$', '', line) for line in lines_of_text]
def file_contains_function(filename, function_name):
"""Check if file contains a function call"""
lines_of_text = read_py_as_text(filename)
lines_without_comments = remove_comments(lines_of_text)
text = ''.join(lines_without_comments)
search_str = function_name + r'\(.*\)'
list_of_calls = re.findall(search_str, text)
return list_of_calls != []
def nested_type_sensitive_equals(val1, val2):
if not val1 == val2:
return False
elif not type(val1) == type(val2):
return False
elif type(val1) == list or type(val1) == tuple:
for el1, el2 in zip(val1, val2):
if not nested_type_sensitive_equals(el1, el2):
return False
return True
def error_message_dict(module, correct_output_type, function_name):
"""Return a dict of all basic error messages"""
msg_dict = {}
msg_dict['passed'] = """
WELL DONE! Your solution passed all the tests.
"""
msg_dict['print_warning'] = """
WARNING! Your function contains a print statement.
This is nearly always a bad idea, unless the purpose
of the function is to print something.
"""
msg_dict['forbidden_function'] = """
ERROR! Your function uses the following
function/method, which is not allowed here: """
msg_dict['none'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
"""
msg_dict['none_plus_print'] = f"""
ERROR! Your function returns None.
This probably means that you forgot a return statement.
Perhaps your function prints the {correct_output_type} instead of returning it?
"""
msg_dict['wrong_type'] = f"""
ERROR! Your function does not return a {correct_output_type}.
Instead, it returns a value of type: """
msg_dict['correct_type_but_wrong'] = f"""
ERROR! Your function returns a {correct_output_type},
but the {correct_output_type} is not quite correct.
Run the example code and compare your printout to the expected printout.
"""
msg_dict['hardcoded'] = f"""
ERROR! Your function works for the examples in the exercise text,
but not when other values are used as input parameters.
Ensure that your function works correctly for general inputs.
"""
msg_dict['no_module'] = f"""
No module named {module} was found. Check that you saved your
function in {module}.py and that this test is located
in the same directory as {module}.py.
"""
msg_dict['attribute_error'] = f"""
The module {module} does not contain a function
named {function_name}. Check for spelling
mistakes in the function name.
"""
msg_dict['timeout_error'] = f"""
Your function did not return within 5 seconds.
The most likely explanation for this is that you have created an
infinite loop. Double-check any while loops!
"""
msg_dict['modifies_input_dict_with_lists'] = f"""
ERROR! Your function modifies the input dictionary.
Make sure to:
1. create a new dictionary instead of modifying the input dictionary
2. create (sorted) copies of the lists in the original dictionary
instead of sorting the original lists.
"""
return msg_dict
def print_inputs_and_output(inputs, output, correct_output, arg_names, function_name):
if type(output) == str:
output = "'" + output + "'"
if type(correct_output) == str:
correct_output = "'" + correct_output + "'"
print(' The following call:\n')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]} = {arg}')
arg_string = '(' + ', '.join(arg_names) + ')'
print(f' {function_name}{arg_string}')
print(f'\n returns: {output}.')
print(f'\n The correct value is: {correct_output}.\n')
def print_modified_inputs(inputs, orig_inputs, arg_names, function_name):
print(f' Before calling {function_name}:')
for index, arg in enumerate(orig_inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print(f'\n After calling {function_name}:')
for index, arg in enumerate(inputs):
if type(arg) == str:
arg = "'" + arg + "'"
print(f' {arg_names[index]}: {arg}')
print('')
def run_student_fun(module, module_to_import, function_name, inputs):
# Import student module
imported_module = importlib.import_module(module)
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
imported_function = getattr(imported_module, function_name)
output = imported_function(*inputs)
def run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions=[], error_msg_if_modifies_input=''):
"""Run visible and hidden tests"""
function_contains_print = False
function_contains_forbidden_func = False
end_of_test_str = '--- End of test ---\n'
# Loop over modules only needed when developing tests
for module in modules:
msg_dict = error_message_dict(module, correct_output_type, function_name)
print(f'--- Testing {module} ---')
# Import student module
try:
imported_module = importlib.import_module(module)
except ModuleNotFoundError:
print(msg_dict['no_module'])
print(end_of_test_str)
continue
# Check for print statements
if file_contains_function(module + '.py', 'print'):
print(msg_dict['print_warning'])
function_contains_print = True
# Check for forbidden function calls
for func in forbidden_functions:
if file_contains_function(module + '.py', func):
function_contains_forbidden_func = True
msg = msg_dict['forbidden_function'] + func + '.'
print(msg)
if function_contains_forbidden_func:
print('\n' + end_of_test_str)
continue
# Import any additional python modules in case the student left that code out
imported_extra_module = importlib.import_module(module_to_import)
exec(f'imported_module.{module_to_import} = imported_extra_module')
# Get student function from the imported module
try:
imported_function = getattr(imported_module, function_name)
except AttributeError:
print(msg_dict['attribute_error'])
print(end_of_test_str)
continue
# Loop over all visible tests
passed_visible = True
for orig_inputs, correct_output in zip(visible_inputs, visible_correct_output):
inputs = copy.deepcopy(orig_inputs)
process_inputs = copy.deepcopy(orig_inputs)
# Call function
process_args = (module, module_to_import, function_name, process_inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_visible = False
print(msg_dict['timeout_error'])
process.terminate()
process.join()
break
output = imported_function(*inputs)
if not nested_type_sensitive_equals(output, correct_output):
passed_visible = False
# Function returns None
if output is None:
if function_contains_print:
print(msg_dict['none_plus_print'])
else:
print(msg_dict['none'])
break
# Function returns the wrong data type (but not None)
elif not(type(output) == type(correct_output)):
msg = msg_dict['wrong_type']
msg += str(type(output)) + '\n'
print(msg)
break
# Function returns correct data type, but wrong
elif not nested_type_sensitive_equals(output, correct_output):
msg = msg_dict['correct_type_but_wrong']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
# Function modifies inputs (and is not supposed to do that)
if inputs != orig_inputs and error_msg_if_modifies_input != '':
passed_visible = False
msg = msg_dict[error_msg_if_modifies_input]
print(msg)
print_modified_inputs(inputs, orig_inputs, arg_names, function_name)
break
# Function reproduces example printouts. Test hidden tests too.
if passed_visible:
# Loop over hidden tests
passed_hidden = True
for inputs, correct_output in zip(hidden_inputs, hidden_correct_output):
# Call function
process_args = (module, module_to_import, function_name, inputs)
process = multiprocessing.Process(target=run_student_fun, args=process_args)
process.start()
process.join(5)
if process.is_alive():
passed_hidden = False
print(msg_dict['timeout_error'])
process.kill()
process.join()
break
output = imported_function(*inputs)
# Failed a hidden test
if not nested_type_sensitive_equals(output, correct_output):
passed_hidden = False
msg = msg_dict['hardcoded']
print(msg)
print_inputs_and_output(inputs, output, correct_output, arg_names, function_name)
break
if passed_hidden:
print(msg_dict['passed'])
print(end_of_test_str)
if __name__ == '__main__':
#--- Excercise details ---
modules = ['A10']
function_name = 'dna_to_rna'
correct_output_type = 'string'
module_to_import = 'math'
arg_names = ['dna']
forbidden_functions = []
#-----------------------------------
#--- Lists of inputs and correct outputs for visible tests ---
visible_inputs = [['GTCCCTAGGGGACTCACAATTGAAGTGGCA']]
visible_correct_output = ['GUCCCUAGGGGACUCACAAUUGAAGUGGCA']
#-----------------------------------
#--- Lists of inputs and correct outputs for hidden tests ---
hidden_inputs = [['ACAATTGATG']]
hidden_correct_output = ['ACAAUUGAUG']
#-----------------------------------
run_tests(modules, correct_output_type, function_name, module_to_import,
visible_inputs, visible_correct_output, hidden_inputs, hidden_correct_output,
arg_names, forbidden_functions)
If you do use any of the testing scripts on this page, we would greatly appreciate if you could provide some feedback by filling out this 1-minute anonymous survey.
Example solutions
# Alternative 1: looping over all characters
def dna_to_rna(dna):
rna = ''
for char in dna:
if char == 'T':
rna += 'U'
else:
rna += char
return rna
# Alternative 2: using the string method replace
def dna_to_rna(dna):
rna = dna.replace('T', 'U')
return rna
Common mistakes
Incorrect use of global variables
print
instead ofreturn