> Given all of this, here is what I believe is the sequence of execution in Baptiste Mispelon's example:
Yep, all correct. Great example for a blog post on my todo list, too.
> (Python 3.13 does print a nice stack trace [that] points to the whole set of 'from ...' statements.)
As far as I can tell, Python has always offered this stack trace. Starting in 3.7, the ultimate error message indicates the module and filename where the import failed, and starting in 3.8 it adds a "(most likely due to a circular import)" advisory to the message and notes that the module is "partially initialized". (It can detect that the top-level code of the partially initialized module is on the stack, which establishes that there is a circular import; but it doesn't prove that the specific name-binding failed because of the circular import. As noted in TFA, some circular imports work, and it's also possible to just have a typo in a name being imported.)
> When I first did this I was quite puzzled until the penny dropped. What's happening is that running 'python a.py' isn't creating an 'a' module but instead a __main__ module, so b.py doesn't find a sys.modules["a"] when it starts and instead creates one and starts loading it. That second version of a.py, now in an "a" module, is what tries to refer to b.B and finds it not there (yet).
Also correct, and long observed (see e.g. https://stackoverflow.com/questions/744373). I could have sworn I've seen a specific reference for the problem but I can't find it now; normally it comes up in the more general context of understanding circular imports.
> Given all of this, here is what I believe is the sequence of execution in Baptiste Mispelon's example:
Yep, all correct. Great example for a blog post on my todo list, too.
> (Python 3.13 does print a nice stack trace [that] points to the whole set of 'from ...' statements.)
As far as I can tell, Python has always offered this stack trace. Starting in 3.7, the ultimate error message indicates the module and filename where the import failed, and starting in 3.8 it adds a "(most likely due to a circular import)" advisory to the message and notes that the module is "partially initialized". (It can detect that the top-level code of the partially initialized module is on the stack, which establishes that there is a circular import; but it doesn't prove that the specific name-binding failed because of the circular import. As noted in TFA, some circular imports work, and it's also possible to just have a typo in a name being imported.)
> When I first did this I was quite puzzled until the penny dropped. What's happening is that running 'python a.py' isn't creating an 'a' module but instead a __main__ module, so b.py doesn't find a sys.modules["a"] when it starts and instead creates one and starts loading it. That second version of a.py, now in an "a" module, is what tries to refer to b.B and finds it not there (yet).
Also correct, and long observed (see e.g. https://stackoverflow.com/questions/744373). I could have sworn I've seen a specific reference for the problem but I can't find it now; normally it comes up in the more general context of understanding circular imports.