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#
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#
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:
python -m pip list --outdated
python -m pip install --upgrade -r requirements.txt
Fix 2: fix it, if the code is yours#
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:
from collections.abc import Callable, Mapping, Iterable, Sequence
isinstance(handler, Callable)
For a simple callable check, the built-in is simpler still:
if callable(handler):
handler()
Fix 3: a compatibility shim, as a last resort#
When the package is unmaintained and you cannot replace it today:
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:
from collections import Counter, defaultdict, deque, namedtuple, OrderedDict
Finding every occurrence#
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.
The related removals#
Python 3.10 to 3.12 removed several other long-deprecated names. If you hit one, you may hit the others:
"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#
# 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.
# 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#
- AttributeError for numpy typeDict — the same pattern, different library.
- Python virtual environments — isolating dependency versions.
- Why is my Python code not working? — reading tracebacks properly.