PythonMastery
reference 3 min read · lesson 9 of 45 in Errors

AttributeError: module 'X' has no attribute 'Y'

1 · The lesson

read

What this error means

You imported a module successfully, but the function or class you tried to use isn't inside it. Python found the module file, just not the name you asked for. This is different from ModuleNotFoundError — the module loaded fine; only the thing inside it is missing.

When you see it

python
Traceback (most recent call last):
  File "main.py", line 2, in <module>
    os.fakeattr()
AttributeError: module 'os' has no attribute 'fakeattr'

Why it happens

Five common causes, roughly in order of frequency:

1. Typo or wrong case. os.getEnv instead of os.getenv.
2. Version mismatch. The function exists in a newer (or older) version of the library. np.float was removed in NumPy 1.20; asyncio.coroutine was removed in Python 3.10.
3. Wrong module. You confused two libraries with similar names — pickle vs cPickle, urllib vs urllib2 vs urllib3, Image (PIL) vs image (matplotlib).
4. Circular import. Module a imports b which imports a again — at the moment the second import runs, a is only half-loaded and the name you want hasn't been defined yet.
5. Local file shadowing the real library. You have a file called random.py in your project, so import random; random.choice(...) looks inside your file, not the stdlib.

How to fix it

Check the spelling against the official docs, and use dir(module) to see what's actually there:

python
import os
print([name for name in dir(os) if name.startswith("get")])

Pin or upgrade the library version if the function was added/removed:

bash
pip install "numpy>=1.20"
pip show numpy           # check installed version

Rename a local file that shadows the stdlib:

python
mv random.py my_random.py
rm random.pyc __pycache__/random.*

Break the circular import by moving the shared import inside a function, or by restructuring so neither module depends on the other at import time:

python
def use_b():
    from b import helper    # import lazily, inside the function
    helper()

Use getattr with a default when you genuinely don't know whether the attribute exists across versions:

python
fn = getattr(some_module, "new_api", some_module.old_api)
+ setup added so this can run · defines some_module
# Lightweight mock for objects whose attributes/methods aren't critical
class _AutoMock:
    def __init__(self, name='mock'): self._name = name
    def __getattr__(self, k): return _AutoMock(self._name + '.' + k)
    def __call__(self, *a, **kw):
        print('-> ' + self._name + '() called')
        return _AutoMock(self._name + '()')
    def __repr__(self): return '<mock ' + self._name + '>'
    def __str__(self): return '<mock ' + self._name + '>'
    def __bool__(self): return True
    def __iter__(self): return iter([])
    def __len__(self): return 0
    def __getitem__(self, k): return _AutoMock(self._name + '[...]')
    def __setitem__(self, k, v): pass
    def __enter__(self): return self
    def __exit__(self, *a): return False
    async def __aenter__(self): return self
    async def __aexit__(self, *a): return False
    def __add__(self, o): return self
    def __radd__(self, o): return self
    def __sub__(self, o): return self
    def __mul__(self, o): return self
    def __rmul__(self, o): return self
    def __truediv__(self, o): return self
    def __eq__(self, o): return isinstance(o, _AutoMock)
    def __hash__(self): return hash(self._name)
    def __lt__(self, o): return True
    def __le__(self, o): return True
    def __gt__(self, o): return False
    def __ge__(self, o): return False
    def __mro_entries__(self, bases): return (object,)

some_module = _AutoMock('some_module')

When you'd actually see this in real code

  • You wrote np.float64 correctly but a tutorial used the removed np.float alias.
  • A requests.session lowercase typo when you meant requests.Session.
  • A file named email.py in your project breaks import email from the stdlib.
  • After upgrading Django from 3.x to 4.x, django.utils.translation.ugettext is gone (renamed to gettext).
  • A circular import between models.py and views.py in a Flask/Django app.

See Also