[Import-SIG] Dabeaz's weird import discovery

Nick Coghlan ncoghlan at gmail.com
Sat Apr 25 05:42:13 CEST 2015

On 23 April 2015 at 02:21, Ethan Furman <ethan at stoneleaf.us> wrote:
> On 04/22, Barry Warsaw wrote:
>> Poking around in Lib/importlib/_bootstrap.py, I think you can see where this
>> happens.  In _find_and_load_unlocked(), 'round about line 2224 (in 3.5's
>> hg:95593), you see this:
>>     if parent:
>>         # Set the module as an attribute on its parent.
>>         parent_module = sys.modules[parent]
>>         setattr(parent_module, name.rpartition('.')[2], module)
>> It's clearly intentional, and fundamental to importlib so I don't think it's
>> dependent on finder or loader.  No matter how it happens, if a submodule is
>> imported, its parent namespace gets a name binding to the submodule.
> I see that it's intentional, but why is it fundamental?

The behaviour serves to preserve the equivalence of the following two
snippets of code in terms of their impact on the "foo" namespace:

    from foo.bar import baz


    import foo.bar
    baz = foo.bar.baz

The only difference between the two is that the latter also binds
"foo" locally, while the from-import doesn't.

The explicit relative import case is just slightly more surprising as
the implied "foo" that is having a submodule added is the package
namespace that's currently executing:

    from .bar import baz

When we break the invariant that "foo.bar" existing in sys.modules
implies there's a "bar" attribute on the parent "foo" module, all
sorts of code tends to get upset (hence the weird errors folks can get
in the face of circular imports, and why we finally relented and made
from-import fall back to checking in sys.modules if it encounters a
missing attribute)


Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia

More information about the Import-SIG mailing list