Skip to content
Happy Programming Guide
Start learning
Python

AttributeError: Module ‘collections’ Has No Attribute ‘Callable’

The abstract base classes moved from collections to collections.abc in Python 3.10. Here is how to identify the outdated package and the three ways to fix it.

Hands typing on a laptop keyboard

AttributeError: module 'collections' has no attribute 'Callable' means some code is using an alias that Python removed in version 3.10. The abstract base classes — Callable, Mapping, Iterable and the rest — moved to collections.abc in Python 3.3, were deprecated for years, and are now gone. Almost always the offending code is in a package you did not write.

What it looks like#

Output
Traceback (most recent call last):
  File "app.py", line 3, in <module>
    import somepackage
  File ".../site-packages/somepackage/utils.py", line 18, in <module>
    if isinstance(handler, collections.Callable):
                           ^^^^^^^^^^^^^^^^^^^^
AttributeError: module 'collections' has no attribute 'Callable'

Note where the failing line is: inside site-packages, not in your file. The folder name straight after site-packages is the package that needs attention.

Why it happened#

The abstract base classes always lived in collections.abc. For backward compatibility they were also reachable directly from collections, with a deprecation warning from Python 3.3 onwards. Python 3.10 removed the aliases.

So this is a package that has not been updated in some years meeting a current Python.

Fix 1: upgrade the package#

Terminal
python -m pip install --upgrade somepackage

Nearly every maintained library fixed this well before Python 3.10 shipped. Upgrading is the correct answer because it moves you forward instead of freezing something old in place.

If several packages are involved, upgrade them together so pip can resolve a consistent set:

Terminal
python -m pip list --outdated
python -m pip install --upgrade -r requirements.txt

Fix 2: fix it, if the code is yours#

Python
import collections
import collections.abc

# Old
isinstance(handler, collections.Callable)
isinstance(config, collections.Mapping)

# New
isinstance(handler, collections.abc.Callable)
isinstance(config, collections.abc.Mapping)

Or import the names directly, which reads better:

Python
from collections.abc import Callable, Mapping, Iterable, Sequence

isinstance(handler, Callable)

For a simple callable check, the built-in is simpler still:

Python
if callable(handler):
    handler()

Fix 3: a compatibility shim, as a last resort#

When the package is unmaintained and you cannot replace it today:

Python
import collections
import collections.abc

# Restore the removed aliases before importing the old package
for name in ("Callable", "Mapping", "MutableMapping", "Sequence",
             "Iterable", "Iterator", "Set", "Hashable"):
    if not hasattr(collections, name):
        setattr(collections, name, getattr(collections.abc, name))

import theoldpackage      # import AFTER the shim

Two things matter. The shim must run before the offending package is imported, so it belongs at the very top of your entry point. And it deserves a comment explaining why it exists and what would let you delete it — unexplained monkey-patches are how temporary fixes become permanent.

The names that moved#

Removed from collections Now in collections.abc
collections.Callable collections.abc.Callable
collections.Mapping collections.abc.Mapping
collections.MutableMapping collections.abc.MutableMapping
collections.Sequence collections.abc.Sequence
collections.Iterable collections.abc.Iterable
collections.Iterator collections.abc.Iterator
collections.Set collections.abc.Set
collections.Hashable collections.abc.Hashable

What remains in collections itself is the concrete containers, and they are unaffected:

Python
from collections import Counter, defaultdict, deque, namedtuple, OrderedDict

Finding every occurrence#

Terminal
grep -rn "collections\.\(Callable\|Mapping\|Iterable\|Sequence\|Set\|Hashable\)" .

Run it across your own code and, if you need to know the extent of the problem, across site-packages too. Anything in your own code you can fix directly.

Python 3.10 to 3.12 removed several other long-deprecated names. If you hit one, you may hit the others:

Python
"abc".decode()                  # gone - str has no decode
inspect.getargspec()            # use inspect.signature()
imp                             # use importlib
distutils                       # use setuptools or packaging
asyncio.coroutine               # use async def

The pattern is the same in each case: a deprecated alias finally removed, and a package that never updated.

Preventing it#

Terminal
# See deprecation warnings before they become errors
python -W error::DeprecationWarning script.py

# Or for a specific module
python -W error::DeprecationWarning::somepackage script.py

Deprecation warnings are hidden by default in normal runs. Turning them on in development and in your test suite gives you years of notice rather than a surprise on the day you upgrade Python.

Python
# In pytest configuration
filterwarnings = ["error::DeprecationWarning"]

Questions people ask#

Which Python version removed it?

Python 3.10. It was deprecated from 3.3 onwards, so the code had a long notice period.

Why does the error name a package I never installed?

It is a transitive dependency — something you installed depends on it. pip show yourpackage lists what requires what.

Is the shim safe?

It restores exactly what used to exist, so behaviour is unchanged. The risk is that it hides a package which is unmaintained in other ways too. Treat it as a stopgap while you find a replacement.

Should I pin Python instead?

Only briefly. Pinning to an old Python means missing security fixes and eventually conflicting with everything else. Fix the dependency.

Where to go next#

Virtual environments, so one project's upgrade cannot break anotherRead next

Keep reading

Python

Data Visualization in Python

Choosing the right chart, then building it with matplotlib, pandas or seaborn — plus the design changes that make a chart readable.

5 min read

Python

Python Security

The security work that matters in Python: SQL injection, pickle, subprocess, secrets, dependency scanning and the randomness mistake.

5 min read

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *