You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Module X has no attribute Y when using star imports #4930
mypy seems to emit a spurious warning that it can't find an attribute of a module that should be imported via a star import and where another import refers to it.
Input
$ head mypackage/*
==> mypackage/__init__.py <==
frommypackage.submod1import*# * -> HELLO and error goes awayfrommypackage.submod2importgreet# comment out and error goes away__all__= ['HELLO', 'greet'] # does nothing
$ mypy mypackage
mypackage/submod2.py:1: error: Module 'mypackage' has no attribute 'HELLO'
$ python -c "import mypackage;mypackage.greet()"# Python has no problems
hello there
$ python --version; mypy --version
Python 3.6.4
mypy 0.590
(haven't checked with master, but I couldn't find any recently closed issues that seemed to talk about something like this)
Expected Output
No error from mypy.
As per the comments in the __init__.py, if some of the imports are tweaked, then mypy is happy. It doesn't seem to be sated by an __all__ to help the star import. I don't quite understand why not importing mypackage.submod2 causes the error to go away as well; it's not like there's a circular import between 1 and 2 where the names in the module aren't defined before it imports (i.e. if you moved the ...import greet line above the other, Python would choke)
I looked into this for a bit. Here is a test that reproduces the problem: JelleZijlstra@6b124e2. The problem goes away when one uses from b import HELLO instead of from b import *.
I think what's going on here is as follows:
semantic analyzer pass 1 adds names from import_from, but not import_alll (because at that point we don't know anything about other modules yet)
semantic analyzer pass 2 adds names from import_all, relying on what was added in pass 1. But if we're trying to import from a module that uses import_all, and pass 2 hasn't run on that module yet, we can't see the names from the import_all, and we get an error.
This could be fixable with an additional semanal pass or something like a callback in pass 1 that runs when the module we're import_alling from is processed, and adds names to the module that is using import_all.
My branch master...JelleZijlstra:circularstar has a fix for the original bug now, but it introduces various new bugs. I don't understand for now what's going on in some of those tests. I'll take another look at this later.
The reason why my code is buggy is that if there is a circular dependency involving import * (as in this test: cb20eaf), I end up adding an ImportedName in both modules, so mypy believes the name exists in either both files or neither file.
This doesn't seem easy to solve. Strictly speaking, mypy should follow the same sequence as Python does at import time: start with one file, then if there is an import, switch to the imported file, then return to the original file. But that would require a full rewrite of pass 1 of semantic analysis and probably a broader change in mypy's build pipeline.
Another solution could be to declare that mypy does not support import * in an import cycle and throw an error that makes this explicit.
Clarification: we'd document that we don't support `import *` *in an import
cycle*. It seems expendable, since you can add an explicit import where
necessary.
This might be fixed by the new semantic analyzer (experimental version available on GitHub mypy master by using --new-semantic-analyzer). It generally improves the processing of imports within import cycles.
If you do decide to drop support for import * in an import cycle, it would be great to at least inform the user about the source of the cycle. In my case it's not obvious, and it could be (???) caused by imports inside a TYPE_CHECKING block, which I can't remove without invalidating my annotations.
I would really rather mypy did not drop support for import * in a cycle.
Any update on this? If anything, there really should be something to tell the user what the issue is, as I just spent 20 minutes trying to figure out the cause.
Though, best would be if from module import * would be supported, at least when __all__ is defined. For now you have to either manually import everything or manually re-type every field, even though the information is there.
Also, as a note: pylint seems completely fine with this behavior.
Reacted by selurvedu, Thomas Mansencal, auxsvr, Oliver Wilkes, Marcel Wilson and Fran Hrženjak
Activity
jacebrowning commentedon May 14, 2018
I can still reproduce this with
mypy 0.600.JelleZijlstra commentedon May 14, 2018
I looked into this for a bit. Here is a test that reproduces the problem: JelleZijlstra@6b124e2. The problem goes away when one uses
from b import HELLOinstead offrom b import *.I think what's going on here is as follows:
import_from, but notimport_alll(because at that point we don't know anything about other modules yet)import_all, relying on what was added in pass 1. But if we're trying to import from a module that uses import_all, and pass 2 hasn't run on that module yet, we can't see the names from the import_all, and we get an error.This could be fixable with an additional semanal pass or something like a callback in pass 1 that runs when the module we're import_alling from is processed, and adds names to the module that is using import_all.
JelleZijlstra commentedon May 14, 2018
My branch master...JelleZijlstra:circularstar has a fix for the original bug now, but it introduces various new bugs. I don't understand for now what's going on in some of those tests. I'll take another look at this later.
JelleZijlstra commentedon May 15, 2018
The reason why my code is buggy is that if there is a circular dependency involving
import *(as in this test: cb20eaf), I end up adding an ImportedName in both modules, so mypy believes the name exists in either both files or neither file.This doesn't seem easy to solve. Strictly speaking, mypy should follow the same sequence as Python does at import time: start with one file, then if there is an import, switch to the imported file, then return to the original file. But that would require a full rewrite of pass 1 of semantic analysis and probably a broader change in mypy's build pipeline.
Another solution could be to declare that mypy does not support
import *in an import cycle and throw an error that makes this explicit.gvanrossum commentedon May 15, 2018
sebastian-philipp commentedon Mar 26, 2019
not supporting
import *is a bit unfortunate. 😞. I guess we can live with it. Needs proper documentation, though.gvanrossum commentedon Mar 26, 2019
sebastian-philipp commentedon Mar 26, 2019
ok, the effect I'm seeing here is then caused by something else. thanks!
Click to expand
JukkaL commentedon Mar 26, 2019
This might be fixed by the new semantic analyzer (experimental version available on GitHub mypy master by using
--new-semantic-analyzer). It generally improves the processing of imports within import cycles.chadrik commentedon Apr 19, 2019
I have the same issue. I tried with
0.710+dev.f0491839adf2db2da74768b1d01f8e244e769f59using--new-semantic-analyzerand it did not fix the problem.chadrik commentedon Apr 19, 2019
If you do decide to drop support for
import *in an import cycle, it would be great to at least inform the user about the source of the cycle. In my case it's not obvious, and it could be (???) caused by imports inside aTYPE_CHECKINGblock, which I can't remove without invalidating my annotations.I would really rather mypy did not drop support for
import *in a cycle.JukkaL commentedon May 21, 2019
We are not planning to drop support for
import *in import cycles, but there may be specific use cases that are too hard to support.JukkaL commentedon Jul 3, 2019
I'm dropping the new-semantic-analyzer label since this doesn't seem specific to it.
nicktimko commentedon Jul 3, 2019
Would it be any easier to constrain support for
import *in a cycle where the*imported module/package defines a (static)__all__?marmistrz commentedon Jun 3, 2021
This happens while using pygame:
results in
even though the constant is there. The type hint is also properly resolved by VS Code. The constant is explicitly re-exported using
__all__.bczsalba commentedon Aug 11, 2021
Any update on this? If anything, there really should be something to tell the user what the issue is, as I just spent 20 minutes trying to figure out the cause.
Though, best would be if
from module import *would be supported, at least when__all__is defined. For now you have to either manually import everything or manually re-type every field, even though the information is there.Also, as a note: pylint seems completely fine with this behavior.
ilevkivskyi commentedon Nov 15, 2022
FWIW the original example passes cleanly on current master (both with and without
__all__). So closing this for now.