Computing lab demo tutorial: how to read feedback messages¶
If you prefer a video over reading the text below, you can watch video 42.
Introduction¶
For every assignment submissions, there will be (automatic) feedback on the correctness of the submission.
Checks are done by machine and thus objective and fair (i.e. exactly the same testing strictness is employed for all students). They are also strict.
You can submit your assignments at any time of the day. You can submit as often as you like. Ideally, you try to improve your assignment after every submission.
The testing system will stop testing for each task once it has come across a single check that has failed. Once you have fixed that problem, you should submit your assignment again.
The auto testing is carried out by a machine and as such the feedback it provides is expressed in a particular way that we need to learn to interpret.
This page and the examples below provide training how to read the protocol from failed checks, and how to understand what the error is that the autotesting system complains about. (This will not explain what is wrong in your code, it will just say that the way the code reacts to particular input values appears to be wrong.)
We encourage students to ask demonstrators in the laboratory sessions to explain the error messages you get from the feedback system, in particular in the beginning of the course.
How to read the feedback (if your solution is not perfect)¶
The messages from the autotesting system can tell you exactly the symptom of a problem/bug/missing feature in your code, and you should understand and use that information to use your time effectively when trying to debug your code. In principle, you should try to repeat the test that the testing system carried out at your computer, and then debug your code until that test produces the right output. We show some examples below.
We have tried to help you by adding comments in the testing code that outline what is being tested. These may be helpful to read as well. You may need to scroll up a little in the reported error from the testing system to come to understand what test is being carried out. Comments are line that start with a hash (
#
)While the actual testing is carried out by a machine, the tests have been written by a human. It is possible that one or more are incorrect.
If you do not understand why your program fails a particular test, then there is a chance that the test is wrong. Please get in touch with demonstrators/lecturer to address this, and we will investigate.
This page: walk through of add
exercise from demo
assignment¶
On this page, we look at a hypothetical task of the lab called
demo where students are asked to implement a function add
.
We show a correct solution (i.e. a function add
) to the task, and
a number of incorrect solutions (each of which contains a mistake).
For all of these, we show the feedback that the feedback system would provide. In particular for the first few examples, we explain in great detail how to read the feedback from the system.
The task¶
Imagine we are given the following task:
Write a function
add(a, b)
that takes two numbersa
andb
and returns their sum.
Correct solution¶
A possible solution is given here:
def add(a, b):
"""This is my add function. It returns the sum of a and b."""
return a + b
There is nothing wrong with this solution. The testing system would provide a report (by email), that looks like this:
Testing of your submitted code has been completed:
Overview
========
add passed -> 1
Success rate for this lab: 1/1 = 100.0%
This tells us that one exercise has been tested (the exercise function was
called add
), and this has been tested ok
. This means there were no
mistakes found in that function.
Most labs have more than one exercises per submission, so there will be multiple lines of pass/fail information: one for each exercise.
In the following, we describe a number of mistakes and the associated error messages.
Function returning wrong value (function carrying out wrong calculation)¶
Imagine you have misunderstood the question and written the following code which
computes the average value of a
and b
(where the sum was requested):
def add(a, b):
"""This is my function. It returns the mean of a and b."""
return (a + b) * 0.5
The test system will provide a report like this:
1 Overview
2 ========
3
4 add failed -> 0
5
6 Success rate for this lab: 0/1 = 0.0%
7
8
9 Detailed test report:
10
11 ============================= test session starts ==============================
12 platform linux -- Python 3.12.6, pytest-8.3.3, pluggy-1.5.0 -- /.pixi/envs/default/bin/python3.12
13 rootdir: /io
14 plugins: reportlog-0.1.2, timeout-2.3.1
15 timeout: 10.0s
16 timeout method: signal
17 timeout func_only: False
18 collecting ... collected 1 item
19
20 test_add.py::test_add FAILED [100%]
21
22
23 def test_add():
24 assert s.add(0, 0) == 0
25 > assert s.add(1, 2) == 3
26 E assert 1.5 == 3
27 E + where 1.5 = <function add at 0x7ff136713560>(1, 2)
28 E + where <function add at 0x7ff136713560> = s.add
29
30
31 test_add.py:14: AssertionError
32 -------------- generated report log file: pytest_reportlog.jsonl ---------------
33 =========================== short test summary info ============================
34 FAILED test_add.py::test_add - assert 1.5 == 3
35 + where 1.5 = <function add at 0x7ff136713560>(1, 2)
36 + where <function add at 0x7ff136713560> = s.add
37 ============================== 1 failed in 0.05s ===============================
We have added line numbers to the output above to make the explanation easier:
lines 1 provides a summary of all questions ‘passed’ (i.e. no mistake) or ‘failed’ (i.e. some mistake).
line 5 shows a list of all exercises and for each of those whether they have passed or failed. In this case, there was only one exercise (to write the function
add
) and thus there is only line 5 reporting that this function has failed a test.from line 9 down, a detailed report is provided for every question that failed a check. In this case, there is only the testing of the
add
function and the report for this starts in line 12.Lines 11 to 18 include diagnostic information which you can ignore.
Line 20 reminds us that some error was reported when testing the
add
function.the actual code that carries out the testing is written in Python and shown starting in line 31. Because it is TESTing the function ADD, the test function is called
test_add
.the testing system will go through a number of
assert
statements: each of those will check a certain property or behaviour that the functionadd
should fulfil (the condition given to the assert statement should evaluate toTrue
for a check to pass.)the first such
assert
statement is in line 24:assert s.add(0, 0) == 0
the file you submitted is known in the test system as
s
– you can think of this ass
for Submission (ors
for student). The functions.add()
is thus the function you have written and submitted.line 24 the
assert
statementassert s.add(0, 0) == 0
will calls.add
with arguments(0, 0)
and expects that the result of this is the same as 0 (== 0
).Our implementation of
add
is thus called witha=0
andb=0
and will return 0 (because the average of 0 and 0 is zero). This 0 is compared with the other zero (on the right hand sign of the comparison operator==
) and the check is passed.
This first check done here (i.e. checking that 0 + 0 = 0) does not find the bug in the function (which computes
(a + b) * 0.5
instead of the required suma + b
) and is passed.the next check (line 25) will fail though:
line 25: the
assert
statementassert s.add(1, 2) == 3
will calls.add
with arguments (1, 2) and expects that the result of this is the same as 3 (== 3
).
In more detail, the
assert
statementcalls the function
s.add(1, 2)
and replacess.add(1, 2)
with the value that the function returns.the return value of
s.add(1, 2)
is1.5
(as we can see line 27 on the left of the comparison operator==
)the
assert
statement will compare this value (i.e.1.5
) with3
(line 26) and notice that they are not the same, thus raising anAssertionError
(the AssertionError is mentioned in line 31).
the submission system reports that a test has failed. In particular, we need to look for the greater than sign (
>
) in the left most column which points to exactly the assert statement that has failed. In this case, this is line 25.Lines 26, 27 and 28 provide more details on intermediate steps in evaluating the assert statement that has failed.
So what has happened? The code we submitted returns 1.5 when given
a=1
andb=2
because it (wrongly) computes the mean ofa
andb
, rather than the sum. The testing system knows that the sum of 1 and 2 should be 3, and this is exactly the test carried out in line 25.
A single failed assert statement will result in an overall fail
for that exercise. Once a test has failed, no further tests will be
carried out for that function.
In the case here, the question you should ask (to find out what is wrong) is “why does my code return 1.5 where as it should return 3 for input arguments a=1 and b=2”. You can actually copy the test to Python prompt and run:
>>> add(1, 2) == 3
For the code with the bug shown above, this should display:
>>> add(1, 2) == 3
False
where as once the bug is fixed, you should see:
>>> add(1, 2) == 3
True
Often, it may be more instructive to not carry out the comparison, but just display the return value, i.e.
>>> add(1, 2)
where the incorrect response would be:
>>> add(1, 2)
1.5
and the correct answer should read:
>>> add(1, 2)
3
The feedback from the testing system should help you debugging your code.
Function not defined (no attribute add
)¶
Let’s consider another possible mistake. Imagine we have implemented the right code, but given the function a different name:
def add_those_numbers(a, b):
"""This is my add function. It returns the sum of a and b."""
return a + b
The feedback for this submission reads:
1 ___________________________________ test_add ___________________________________
2
3 def test_add():
4 > assert s.add(0, 0) == 0
5 E AttributeError: module 's' has no attribute 'add'
Here, the very first assert statement (line 4) fails: it as trying to call the function
s.add()
with arguments(0, 0)
. However, that function object does not exist: the only function object that does exist ins
(which is the submitted file) is calledadd_those_numbers
.The system thus reports (line 5)
'module' object has no attribute 'add'
.The ‘module’ refers to the module
s
(i.e. your code), and the remainder of the message simply says: there is noadd
in modules
.
As always, the greater than sign (>
) shows the line in which an
assert
statement has failed, and subsequent lines give more
information about the type of failure. In this case, the test fails
because the function that should be tested (i.e. add
) appears to
be missing.
This kind of error will always be raised if a particular question has not been attempted.
Function returning wrong data type (string)¶
The function add
needs to return the value of a+b
. The type of
a
and b
has not been specified in the instructions. We would thus expect the function
to work at least for Python data types int
and long
(both integers),
floating point numbers float
and complex
numbers.
(Dynamic typing will ensure that the return value is of the same type
as the input arguments a
and b
(if a and b are of the same
type), and if the plus operator can deal with all the types.)
Let’s assume the following mistake: the function add
that we submit returns the result of the
calculation as a string
(this may seem like a good idea but it is
not: if the result of a calculation is required as a string, it can be
converted into a string later):
def add(a, b):
"""Takes numbers a and b and returns a+b."""
return str(a + b)
This code would produce the following error message when being auto-tested:
1 ___________________________________ test_add ___________________________________
2
3 def test_add():
4 > assert s.add(0, 0) == 0
5 E AssertionError: assert '0' == 0
6 E + where '0' = <function add at 0x7efbfe537560>(0, 0)
7 E + where <function add at 0x7efbfe537560> = s.add
8
9
10 test_add.py:13: AssertionError
The
>
tells us that theassert
statement that fails is in line 4. It attempts to compares.add(0, 0)
with0
.line 5 (6 and 7) tells us that the call of
s.add(0,0)
results in the value'0.0'
which is the compared against the number0
:assert '0.0' == 0
Note the subtlety that
'0.0'
(on the left hand side of the comparison operator==
) is a string but0
(on the right hand side) is a number.They are thus not the same, and the test fails.
We summarise that it is important to pay close attention to the details as to why a particular assert statement has failed (in this case the clue is in line 5).
Function returning wrong data type (list)¶
A minor variation on Function returning wrong data type (string) is
that the function returns [a + b]
(which is a list with one element, and this element will be
the sum of a
and b
) instead of a + b
:
def add(a, b):
"""Takes numbers a and b and returns a+b."""
return [a + b]
The error message reads:
1 ___________________________________ test_add ___________________________________
2
3 def test_add():
4 > assert s.add(0, 0) == 0
5 E assert [0] == 0
6 E + where [0] = <function add at 0x7fd1bc397560>(0, 0)
7 E + where <function add at 0x7fd1bc397560> = s.add
8
9 test_add.py:13: AssertionError
The revealing line is number 5 which shows that a comparison of
[0]
and 0
is attempted: the list with one element ([0]
) is
what s.add(0, 0)
returned.
Function not returning a value¶
Another common mistake is that a function prints a value to the screen rather than returning it to the calling level using the return
command.
Suppose our function would print the value of a+b
instead of returning it:
def add(a, b):
"""Takes numbers a and b and returns a+b."""
print(a + b)
The error report would read:
1 test_add
2 --------
3 def test_add():
4 > assert s.add(0, 0) == 0
5 E assert None == 0
6 E + where None = <function add at 0x838009c>(0, 0)
7 E + where <function add at 0x838009c> = s.add
8
9 test_demo.py:4: AssertionError
10 ------------------------------- Captured stdout -------------------------------
11 0
the assert in line 4 fails (we know that because of the
>
on the left)line 5 contains the clue: the call of
s.add(0, 0)
evaluates toNone
(the special Python objectNone
).The comparison of
None
with0
fails (i.e.None == 0
evaluates toFalse
).The underlying problem is that the submitted definition of the
add
function does not use thereturn
keyword to return the value ofa + b
(instead the value is printed). Any function not using the return keyword will return the special valueNone
.Remark: Note that in this case (where the function prints something) the new section
Captured stdout
appears in line 10: this shows the0
that was printed whens.add(0, 0)
was called.
Function not having a docstring¶
Suppose the function is implemented correctly, but no docstring has been provided:
def add(a, b):
return a + b
The error message reads:
1 ___________________________________ test_add ___________________________________
2
3 def test_add():
4 assert s.add(0, 0) == 0
5 assert s.add(1, 2) == 3
6 assert s.add(0, 1) == 1
7 assert s.add(1, 1) == 2
8 assert s.add(1.5, 1.0) == 2.5
9
10 # Does the function have a documentation string?
11 > assert s.add.__doc__ is not None
12 E assert None is not None
13 E + where None = <function add at 0x7f28a5023560>.__doc__
14 E + where <function add at 0x7f28a5023560> = s.add
We observe that
all the numerical tests (lines 4 to 8) are passed.
the assert statement in line 11 fails
line 10 contains a comment we have put before the test to give a hint about the following test. It suggests that the test in the next line is to see whether the function has a documentation string.
line 11 reads:
assert s.add.__doc__ is not None
The object that is being tested here is
s.add.__doc__
, i.e. the__doc__
attribute of the functionadd
in the submitted files
.This is the documentation string: if it is defined, it is a string. If it is undefined, this attribute
s.add.__doc__
is the special Python valueNone
.The documentation string is what is displayed when we pass the function object
add
to the help function, for example:>>> help(add)
The comparison operator (
!=
) checks for inequality, i.e.s.add.__doc__ is not None
will evaluate to be
True
ifs.add.__doc__
is NOT None, i.e. if thes.add.__doc__
exists (so all is well, because then the documentation string is defined; and this is what the test is for).If, however,
s.add.__doc__
isNone
(because it was not defined as in our example here), then the whole expressions.add.__doc__ is not None
evaluates toFalse
and thus the assert statement fails.
Occasionally, assert
statements are preceded by Python comments
in the line above (as here where the comment is in line 10 and the
assert statement in line 11). These comments have been placed there to
help understanding what is being tested: they are worth reading –
often there is no need to look into the detailed error message if
that line provides enough context, or at least knowing about the context
will make understanding the purpose of the assert statement easier.
When the source code file has syntax errors¶
The testing system will try to import the student file under the name s
before running any tests.
I.e. if the submitted file is demo.py
, then the testing file will want
to run a command like:
import demo as s
If there are syntax errors in the demo.py
file, this will be impossible, and
the testing cannot even be started. For example, here is a “Python” program with
incorrect syntax:
1 define add(a, b):
2 """This is my function. It returns the mean of a and b."""
3 return a + b
The error is the use of define
instead of the correct term def
in
line 1. (Many other syntax errors can be made, of course.)
For this example (and more generally if the import of the demo.py
file
fails), an email along these lines is returned:
1 Error importing files
2 =====================
3
4 File 'demo.py'
5 --------------
6 Traceback (most recent call last):
7 File "<string>", line 1, in <module>
8 File "/io/s.py", line 1
9 define add(a, b):
10 ^^^
11 SyntaxError: invalid syntax
The error message is trying to tell us that there is a SyntaxError
(line 11)
and the three hats pointing upwards (^^^
) point to to line 9 to indicate the
error has been noticed in that line. (Not always is the error at the point the
interpreter points to, but often it is correct or at least close to the
problem.)
The best way to avoid/fix this type of import error, is to make sure that the
file demo.py
is valid Python, i.e. executing it does not raise any error
messages.
Writing code is easy…¶
… but debugging it is (a lot) harder.
Please seek help for interpreting error messages if needed: the error messages you receive in the feedback are standard Python error messages. Learning how to read those will benefit you for the practical exercises and also far beyond this module.