38

I have:

foo/
├── __init__.py
├── bar.py
└── baz
    ├── __init__.py
    └── alice.py

In bar.py, I import Alice, which is an empty class with nothing in it but the name attribute set to "Alice".

from baz.alice import Alice

a = Alice()
print(a.name)

This runs properly:

$ python foo/bar.py
Alice

But mypy complains:

$ mypy --version
mypy 0.910
$ mypy --strict .
foo/bar.py:1: error: Cannot find implementation or library stub for module named "baz.alice"
foo/bar.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 6 source files)

Why is mypy complaining?

Tom Huibregtse
  • 573
  • 1
  • 5
  • 13
  • 3
    Does it still complain if you fully qualify the import `from foo.baz.alice import Alice`? – paul41 Aug 07 '21 at 20:40
  • mypy becomes happy in that case, but then the python interpreter raises `ModuleNotFoundError: No module named 'foo'`. Maybe I'm missing some other change to fix that? – Tom Huibregtse Aug 08 '21 at 03:14
  • 1
    @TomHuibregtse when you run `python foo/bar.py`, your `PYTHONPATH` does not include the directory where `foo` is. In your `foo/bar.py`, add a `print(sys.path)`. You'll see it includes the `foo/` directory, but it won't include the directory where `foo` itself is (one level up). That means Python has no way of resolving the `foo` package. So you need to add that directory to your `PYTHONPATH` env variable if you really want to run the script from the `foo` folder. – Yu Chen Aug 08 '21 at 06:54
  • Paul41's suggestion worked for me. – Juan Martinez Dec 21 '22 at 10:09

1 Answers1

35

mypy has its own search path for imports and does not resolve imports exactly as Python does and it isn't able to find the baz.alice module. Check the documentation listed in the error message, specifically the section on How imports are found:

The rules for searching for a module foo are as follows:

The search looks in each of the directories in the search path (see above) until a match is found.

  1. If a package named foo is found (i.e. a directory foo containing an __init__.py or __init__.pyi file) that’s a match.
  2. If a stub file named foo.pyi is found, that’s a match.
  3. If a Python module named foo.py is found, that’s a match.

The documentation also states that this in the section on Mapping file paths to modules:

For each file to be checked, mypy will attempt to associate the file (e.g. project/foo/bar/baz.py) with a fully qualified module name (e.g. foo.bar.baz).

There's a few ways to solve this particular issue:

  1. As paul41 mentioned in his comment, one option to solve this issue is by providing the fully qualified import (from foo.baz.alice import Alice), and then running from a top-level module (a .py file in the root level).
  2. You could add a # type: ignore to the import line.
  3. You can edit the MYPYPATH variable to point to the foo directory:
(venv) (base) ➜ mypy foo/bar.py --strict
foo/bar.py:3: error: Cannot find implementation or library stub for module named "baz.alice"
foo/bar.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)
(venv) (base) ➜ export MYPYPATH=foo/     
(venv) (base) ➜ mypy foo/bar.py --strict
Success: no issues found in 1 source file
Yu Chen
  • 6,540
  • 6
  • 51
  • 86
  • 2
    Wouldn't using `# type: ignore` be undesired if I still wanted to have type checking for my use of the `foo.baz.alice` module in `foo.bar`? Also, I like your `PYTHONPATH` solution from above. It allows me to use fully qualified module names. Poetry takes care of adding `foo/` to `PYTHONPATH` as seen by `sys.path` output. – Tom Huibregtse Aug 08 '21 at 12:12
  • 3
    @TomHuibregtse yeah for sure, I'm just giving you ways to solve your error. Ideally you don't want to have to be disabling mypy type checking for specific lines unless you really had to. – Yu Chen Aug 08 '21 at 16:42