First of all you have to understand that:
The sys.modules
variable play the role of a cache in the import mechanize which mean that if we import a module the first time, an entry containing the name of this module is added to sys.modules
, so that when we try to import this same module next time, we will only get the already cached module from sys.modules
and we will not execute the module again.
The difference between import pkg.foo
and from pkg import foo
is that the second is equivalent too: import pkg.foo
followed by getattr(pkg, 'foo')
. (check my answer here for more detail)
Now what happen in your first example is the following:
1- In main.py
: we start by executing the line from pkg import foo
, so first the entry pkg.foo
is added to sys.modules
(i.e. 'pkg.foo' in sys.modules == True
) than we try to import foo
.
2- In foo.py
: While importing foo
we end up executing this line: from pkg import bar
and again the entry pkg.bar
is added to sys.modules
; and than we start importing bar
, what is important to note here is that we are still executing foo.py
, so we are kind off still executing this line from pkg import foo
from main.py
.
3- In bar.py
: while now importing bar.py
we end up executing the line: from pkg import foo
, but remember we have already an entry in sys.modules
for pkg.foo
which mean that executing foo.py
will be skipped and the import mechanize will get us instead the sys.modules['pkg.foo']
entry, and now we will excute getattr(pkg, 'foo')
as explained above, but remember 'pkg.foo' didn't finish importing; which mean that there is no attribute foo
which explain the error.
Now if you put in bar.py
only import pkg.foo
this will not have the line that cause us the problem last time which is: getattr(pkg, 'foo')
.
HTH,