Long story short: rather then having a separate 'stubs' directory, you probably want to move your foo.pyi
file into the top-level trymypy
folder, alongside foo.py
.
Long story long, there are currently two problems with your setup.
The first problem is that the folder structure of your stubs need to mirror the way the underlying code is structured. So since you want to do from trymypy.foo import blah
, you need to adjust your folder structure to look like this:
trymypy/
|- stubs/
| |- trymypy/
| | |- __init__.pyi
| \ \- foo.pyi
|- __init__.py
|- usefoo.py
\- foo.py
You should continue setting the mypypath to point to trymypy/stubs
. You can use either an absolute or a relative path.
The bigger second problem is that your stubs may end up being shadowed and ignored depending on how exactly you're invoking mypy. For example, if you run mypy -p trymypy
from outside of the trymypy
folder, mypy will start by parsing every trymypy
and the two submodules it directly contains (trymypy.__init__
, trymypy.usefoo
, and trymypy.foo
).
And once trymypy.foo
is loaded, mypy will not bother trying to re-load it again, which means it never bothers checking the stubs you specified.
But if you try type-checking individual files (e.g. mypy -m trymypy.usefoo
, mypy -p trymypy.usefoo
), mypy won't try loading everything inside trymypy
, which means it can find the stubs using the typical import resolution rules.
You can confirm the behavior of all of this yourself by passing in the -v
flag, which runs mypy in verbose mode and print out exactly what gets loaded. Be sure to delete the .mypy_cache
directory before each run.
Note: I actually honestly have no idea whether this difference in behavior is intentional or a bug in mypy. The import rules are pretty nuanced.
The fix is thankfully straightforward though: just move your foo.pyi
file into the top-level trymypy
folder like so:
trymypy/
|- __init__.py
|- usefoo.py
|- foo.py
\- foo.pyi
Now, no matter what gets imported in what order, mypy will always find foo.py
and foo.pyi
at the same time, since both files are located in the same directory. And whenever a py
and pyi
file are located within the same directory, the pyi
file always wins out (and the py file is ignored).
You may perhaps have two follow-up questions regarding this new folder structure:
Is there a way to type-check the contents of foo.py
using the type hints present in foo.pyi
?
The answer is no, there currently is not a way. If you have foo.pyi
present, the body of foo.py
is essentially ignored altogether by mypy. There is somebody interested in adding support for this feature though, so you could perhaps subscribe to the linked Github issue for updates.
Create a separate "stubs" folder ended up not being useful here. So when is it useful?
The answer is that it's primarily useful when you want to add stubs for third party libraries. I actually don't have a lot of experience with the "add stubs for your own code" workflow, but my understanding is that such stubs are usually "inlined" in the manner described above.