Adam Johnson

Home | Blog | Training | Projects | Colophon | Contact

Why does Python log a SyntaxWarning saying "object is not callable"?

2020-06-16 Danger snake

Take this code, which we want to return a list of shopping items with desired quantities:

def get_shopping_list():
    return [
        ("bananas", 5)
        ("pears", 3)
    ]

If we import it in Python 3.8+, we’ll see a warning message on line 3:

>>> import example
/.../example.py:3: SyntaxWarning: 'tuple' object is not callable; perhaps you missed a comma?
  ("bananas", 5)

This is a new warning added in Python 3.8. From the release notes:

When a comma is missed in code such as [(10, 20) (30, 40)], the compiler displays a SyntaxWarning with a helpful suggestion. This improves on just having a TypeError indicating that the first tuple was not callable. (Contributed by Serhiy Storchaka in bpo-15248.)

Indeed, if we run the function, it raises a TypeError:

>>> example.get_shopping_list()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../example.py", line 3, in get_shopping_list
    ("bananas", 5)
TypeError: 'tuple' object is not callable

Python emits the SyntaxWarning at import time because it can predict that the code will raise this TypeError. So why does the error occur?

Some objects in Python are callable, such as functions or classes. You normally call them with the syntax: some_object(an_argument, another_argument), but you can also spread a function call over multiple lines, for example:

some_object(
    an_argument,
    another_argument
)

If this happens within parentheses or square brackets, you can also put the call parentheses on a new line:

[
    some_object
    (an_argument, another_argument)
]

This looks odd, and is very rare in Python code, but it’s valid.

Our get_shopping_list() function follows this pattern, unintentionally. We could replace some_object with ("bananas", 5), and (an_argument, another_argument) with ("pears", 3). So the second line, ("pears", 3), is not interpreted as another tuple, but as arguments to a call to the first line. The first line is the tuple ("bananas", 5), and tuple objects aren’t callable, so it raises a TypeError.

The fix is to add a comma after the ("bananas", 5) tuple:

def get_shopping_list():
    return [
        ("bananas", 5),
        ("pears", 3),
    ]

This comma separated the two tuples, so our function can return them in a list, as intended.

Note there’s now also a comma after ("pears", 3) tuple. You should form a habit to always end each item in a multi-line list with a comma, even when it’s the last one. Then you can never encounter the error.

Other Types

Python raises this SyntaxWarning for more types than just tuples. It also raises them for most of the built-in literal types:

You can see this on the console, for example with an f-string:

>>> def example():
...     return f"123"(456)
...
<stdin>:2: SyntaxWarning: 'str' object is not callable; perhaps you missed a comma?

Fin

I hope this helps you understand and fix this error,

—Adam


Are your Django project's tests slow? Read Speed Up Your Django Tests now!


Subscribe via RSS, Twitter, or email:

One summary email a week, no spam, I pinky promise.

Related posts:

Tags: python