Where did the bug probably happen? That is a typical question when looking for errors in program code. Today’s programmer has a whole palette of tools, from editor to debugger, but some bugs still refuse to be cracked easily. They feel almost magical, and the more one focuses on them, the easier they seem to escape. In the Where Did the Bug Probably Happen? series I will show a few such bugs that I looked for and found. Because my main programming language is Python, these bugs will concern Python.
The first part starts lightly with a bug I dealt with the previous week. Imagine this function:
def get_vectors():
vectors = [
(1, 298, 35),
(392, 401, 54),
(325, 4, 8),
(501, 648, 923)
]
return vectorsSo far everything is fine. The function exists, we use it, it returns vectors, and everyone is happy.
Now we want to add another vector (9, 8, 7) at the end of the list:
def get_vectors():
vectors = [
(1, 298, 35),
(392, 401, 54),
(325, 4, 8),
(501, 648, 923)
(9, 8, 7)
]
return vectorsRunning it gives a surprise:
Traceback (most recent call last):
File "test.py", line 11, in <module>
get_vectors()
File "test.py", line 7, in get_vectors
(9, 8, 7)
TypeError: 'tuple' object is not callableThe Bug Itself
The traceback itself does not tell us much, and neither does looking directly at line 7. The problem is one line earlier. Who noticed the missing comma at the end of that line?
The missing comma causes (9, 8, 7) to be interpreted as arguments in a function call, while the tuple on the previous line is treated as the function. A tuple cannot be called as a function in Python.
In this small example the bug is not hard to find. If the elements were function calls or more complex structures, it would be very easy to start looking elsewhere.
Similar Cases
The same fun happens with lists:
def get_vectors():
vectors = [
['jedna', 'dva']
['tri']
]
return vectorsCalling get_vectors() again raises an exception:
Traceback (most recent call last):
File "test.py", line 8, in <module>
get_vectors()
File "test.py", line 4, in get_vectors
['tri']
TypeError: list indices must be integers, not strThe cause is analogous. Instead of confusing a tuple literal with a function call, Python confuses a list literal with indexing.
The bug can be generalised further:
def get_vectors():
vectors = [
[3, 2]
[1]
]
return vectorsThe tricky part is that this code is syntactically valid, semantically valid, and raises no exception at runtime. The result is still far from what we expected: just the list [2].
The Lesson
The lesson is simple. When writing a multiline tuple or list literal in Python, always write a comma after the last element as well. Then the container can be extended later without creating this kind of bug.
The place where this habit will reliably get in the way is JSON: if you leave a trailing comma in JSON, the document is invalid and the parser will refuse to load it.