This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author xdegaye
Recipients Maciej Gol, brett.cannon, eric.snow, ethan.furman, lars.gustaebel, miss-islington, ncoghlan, serhiy.storchaka, xdegaye
Date 2020-01-25.20:20:59
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1579983660.35.0.904605398956.issue39430@roundup.psfhosted.org>
In-reply-to
Content
In msg360620 Serhiy wrote:
> When the interpreter encounters "import foo" in bar.py, the import machinery takes the module foo from sys.modules.
> So you break an infinite cycle and can import modules with cyclic dependencies.

The following pdb session that is run when executing the foo.py script, shows that this is not quite accurate.  When the interpreter encounters "import foo" in bar.py, the import machinery instead starts executing foo.py once again and detects that at line 9 of foo.py the 'import bar' statement does not have to be executed since the import machinery is already in the process of importing this module.  So it is at that point that the infinite cycle is broken.
Later on line 12 in foo.py the import machinery detects that the AttributeError is raised because of a circular import and warns that "partially initialized module 'bar' has no attribute 'BAR' (most likely due to a circular import)".

$ cat -n foo.py
     1  import pdb
     2  debug = 1
     3
     4  # Prevent starting a new pdb session when foo is imported by bar.
     5  if debug and not hasattr(pdb, 'is_pdb_started'):
     6      pdb.Pdb(skip=['importlib*']).set_trace()
     7      pdb.is_pdb_started = True
     8
     9  import bar
    10  FOO = 'foo'
    11  try:
    12      print(bar.BAR)
    13  except AttributeError as e:
    14      # The exception is raised with an explicit reason when foo is imported by
    15      # bar due to partially initialized module 'bar'.
    16      print(e)

$ cat -n bar.py
     1  import foo
     2  BAR = 'bar'
     3  print(foo.FOO)

$ python foo.py
> /tmp/foo.py(7)<module>()
-> pdb.is_pdb_started = True
(Pdb) step
> /tmp/foo.py(9)<module>()
-> import bar
(Pdb) step
--Call--
> /tmp/bar.py(1)<module>()
-> import foo
(Pdb) step
> /tmp/bar.py(1)<module>()
-> import foo
(Pdb) step
--Call--
> /tmp/foo.py(1)<module>()
-> import pdb
(Pdb) step
> /tmp/foo.py(1)<module>()
-> import pdb
(Pdb) step
> /tmp/foo.py(2)<module>()
-> debug = 1
(Pdb) step
> /tmp/foo.py(5)<module>()
-> if debug and not hasattr(pdb, 'is_pdb_started'):
(Pdb) step
> /tmp/foo.py(9)<module>()
-> import bar
(Pdb) step
> /tmp/foo.py(10)<module>()
-> FOO = 'foo'
(Pdb) step
> /tmp/foo.py(11)<module>()
-> try:
(Pdb) step
> /tmp/foo.py(12)<module>()
-> print(bar.BAR)
(Pdb) step
AttributeError: partially initialized module 'bar' has no attribute 'BAR' (most likely due to a circular import)
> /tmp/foo.py(12)<module>()
-> print(bar.BAR)
(Pdb) step
> /tmp/foo.py(13)<module>()
-> except AttributeError as e:
(Pdb) step
> /tmp/foo.py(16)<module>()
-> print(e)
(Pdb) step
partially initialized module 'bar' has no attribute 'BAR' (most likely due to a circular import)
--Return--
> /tmp/foo.py(16)<module>()->None
-> print(e)
(Pdb) step
> /tmp/bar.py(2)<module>()
-> BAR = 'bar'
(Pdb) step
> /tmp/bar.py(3)<module>()
-> print(foo.FOO)
(Pdb) step
foo
--Return--
> /tmp/bar.py(3)<module>()->None
-> print(foo.FOO)
(Pdb) step
> /tmp/foo.py(10)<module>()
-> FOO = 'foo'
(Pdb) step
> /tmp/foo.py(11)<module>()
-> try:
(Pdb) step
> /tmp/foo.py(12)<module>()
-> print(bar.BAR)
(Pdb) step
bar
--Return--
> /tmp/foo.py(12)<module>()->None
-> print(bar.BAR)
(Pdb) step
$
History
Date User Action Args
2020-01-25 20:21:00xdegayesetrecipients: + xdegaye, brett.cannon, ncoghlan, lars.gustaebel, ethan.furman, eric.snow, serhiy.storchaka, miss-islington, Maciej Gol
2020-01-25 20:21:00xdegayesetmessageid: <1579983660.35.0.904605398956.issue39430@roundup.psfhosted.org>
2020-01-25 20:21:00xdegayelinkissue39430 messages
2020-01-25 20:20:59xdegayecreate