ImportError
The import found the module but not what you asked for — a typo, a name that moved or was removed, a relative import outside a package, or a circular import that asked too early.
Demo
import keyword name = 'chain' if not name.isidentifier() or keyword.iskeyword(name): raise ValueError('not a valid name') exec(f'from itertools import {name}') 'imported ' + name
Try batchd and izip_longest: CPython compares the missing name with everything in the module and adds Did you mean: 'batched'? when one is close enough. izip has no close match — it was removed in Python 3 (use the built-in zip). Names are case-sensitive: Chain fails but gets the hint (one case flip is cheap), while CHAIN is five changes away and gets none.
Constructor
| Name | Type | Required | Description |
|---|---|---|---|
| *args | object | no | Usually one message string (also available as e.msg). |
| name | str | None | no (None) | Keyword-only. The module involved — for "cannot import name", the module that lacked the name. |
| path | str | None | no (None) | Keyword-only. The file that triggered the error, when there is one (None for built-in modules). |
Attributes
| Attribute | Type | Meaning |
|---|---|---|
| name | str | None | The module name. For "cannot import name 'x' from 'mod'" this is 'mod'. |
| path | str | None | Path of the module file, or None (built-in modules, relative-import errors). |
| msg | str | The message text, same as str(e) when the exception was built with one argument. |
| args | tuple | The constructor arguments; name and path are not included. |
Common patterns
try: from itertools import batched # Python 3.12+ except ImportError: def batched(iterable, n): it = iter(iterable) while chunk := tuple(islice(it, n)): yield chunk
# orders.py import customers # not: from customers import lookup def total(order): return customers.lookup(order.customer_id).discount
# app/cli.py from .config import load # works with: python -m app.cli
Examples
Pitfalls
import os, sys with open('ie_p1_a.py', 'w') as f: f.write('from ie_p1_b import g\ndef f():\n return 1\n') with open('ie_p1_b.py', 'w') as f: f.write('from ie_p1_a import f\ndef g():\n return f() + 1\n') sys.path.insert(0, os.getcwd()) try: import ie_p1_a except ImportError as e: r = 'circular import' in str(e) finally: sys.path.remove(os.getcwd()) r
import os, sys with open('ie_p2_a.py', 'w') as f: f.write('import ie_p2_b\ndef f():\n return 1\n') with open('ie_p2_b.py', 'w') as f: f.write('import ie_p2_a\ndef g():\n return ie_p2_a.f() + 1\n') sys.path.insert(0, os.getcwd()) try: import ie_p2_a r = ie_p2_a.ie_p2_b.g() finally: sys.path.remove(os.getcwd()) r
from itertools import izip list(izip('ab', 'xy'))
list(zip('ab', 'xy'))
When to use
- Catching it for optional dependencies and version-dependent names (compat shims)
- Raising it (with name=/path=) from a plugin loader when a plugin cannot be loaded
- A bare except ImportError around a big block → it also hides import errors inside the modules you import
- Only the "module not installed" case → catch ModuleNotFoundError
Notes
FAQ
The module Y was found, but has no attribute X. Check, in order: the spelling and case of X; whether X exists in the version of Y you have installed (it may be newer, renamed or removed — compare the library's changelog); whether the Y that was imported is the one you expect (print Y.__file__ — a local file can shadow a package); and whether Y and your module import each other (circular import — the message then says "partially initialized module").