What is the precise rule under which this exception is raised by Python 3 interpreter?
There are plenty of SO questions about that, with excellent answers, but I could not find one that gave a clear, general, and logically precise definition of the circumstances when this exception occurs.
The documentation doesn’t seem to be clear either. It says:
exception
ImportErrorRaised when an import statement fails to find
the module definition or when afrom ... importfails to find a name
that is to be imported.
But this seems inconsistent with the following example.
I meant to ask for a general definition rather than a specific case, but to clarify my concerns, here’s an example:
# code/t.py:
from code import d
# code/d.py
from code import t
Running module t.py from the command line results in ImportError: cannot import name d.
On the other hand, the following code doesn’t raise exceptions:
# code/t.py:
import code.d
# code/d.py
import code.t
At all times, __init__.py is empty.
In this example, the only modules or names mentioned in the import statement are t and d, and they were both clearly found. If the documentation implies that some name within the d module isn’t found, it’s certainly not obvious; and on top of that, I’d expect it to raise NameError: name ... is not defined exception rather than ImportError.
If
abcis a package andxyzis a module, and ifabc‘s__init__.pydefines an__all__that does not includexyz, then you won’t be able to dofrom abc import xyz, but you’ll still be able to doimport abc.xyz.Edit: The short answer is: your problem is that your imports are circular. Modules t and d try to import each other. This won’t work. Don’t do it. I’m going to explain the whole thing, below but the explanation is pretty long.
To understand why it gives an ImportError, try to follow the code execution. If you look at the full traceback instead of just the final part, you can see what it’s doing. With your setup I get a traceback like this (I called the package “testpack” instead of “code”):
You can see what Python is doing here.
t.py, the first thing it sees isfrom testpack import d.d.pyfile to load that module.from testpack import t.t.pyonce, but t as the main script is different than t as a module, so it tries to loadt.pyagain.from testpack import d, which would mean it should try to loadd.py. . . but it already was trying to loadd.pyback in step 2. Since trying to importdled back to trying to importdagain, Python realizes it can’t importdand throws ImportError.Step 4 is kind of anomalous here because you ran a file in the package directly, which isn’t the usual way to do things. See this question for an explanation of why importing a module is different from running it directly. If you try to import
tinstead (withfrom testpack import t), Python realizes the circularity one step sooner, and you get a simpler traceback:Notice that here the error is that it can’t import t. It knows it can’t, because when I told it to import t, it found itself looping back to import t again. In your original example, it didn’t notice it was running t.py twice, because the first time was the main script and the second was an import, so it took one more step and tried to import d again.
Now, why doesn’t this happen when you do
import code.d? The answer is just because you don’t actually try to use the imported modules In this case, it happens as follows (I’m going to explain as if you didfrom code import trather than running it as a script):code.tas imported, even though it’s not done importing yet.import code.d, so it runs d.import code.t, but sincecode.tis already marked as imported, it doesn’t try to import it again.t, it gets to go back and finish loadingt. No problem.The key difference is that the names
tanddare not directly accessible to each other here; they are mediated by the packagecode, so Python doesn’t actually have to finish “deciding what t is” until it is actually used. Withfrom code import t, since the value has to be assigned to the variablet, Python has to know what it is right away.You can see the problem, though if you make
d.pylook like this:Now, after step 2, while running d, it actually tries to access the half-imported module t. This will raise an AttributeError because, since the module hasn’t been fully imported yet, it hasn’t been attached to the package
code.Note that it would be fine as long as the use of
code.tdidn’t happen until afterdfinished running. This will work fine ind.py:You can call
flater and it will work. The reason is that it doesn’t need to usecode.tuntil afterdfinished executing, and after d finishes executing, it can go back and finish executing t.To reiterate, the main moral of the story is don’t use circular imports. It leads to all kinds of headaches. Instead, factor out common code into a third module imported by both modules.