Skip to content

__import__ / importlib.__import__ returned module, top level or not #148813

@calestyo

Description

@calestyo

Documentation

Hey there.

importlib.__import__ does’t seem to have much documentation, but AFAIU it’s intended to be identical to the build-in __import__, for which the documentation says:

When the name variable is of the form package.module, normally, the top-level package (the name up till the first dot) is returned, not the module named by name. However, when a non-empty fromlist argument is given, the module named by name is returned.

While this is strictly speaking true, assuming that the package.module really implies its for absolute modules only, it may be ambiguous, because AFAIU it's not as described for relative modules:

If we look at:

if level == 0:
module = _gcd_import(name)
else:
globals_ = globals if globals is not None else {}
package = _calc___package__(globals_)
module = _gcd_import(name, package, level)
if not fromlist:
# Return up to the first dot in 'name'. This is complicated by the fact
# that 'name' may be relative.
if level == 0:
return _gcd_import(name.partition('.')[0])
elif not name:
return module
else:
# Figure out where to slice the module's name up to the first dot
# in 'name'.
cut_off = len(name) - len(name.partition('.')[0])
# Slice end needs to be positive to alleviate need to special-case
# when ``'.' not in name``.
return sys.modules[module.__name__[:len(module.__name__)-cut_off]]

Then at the beginning module is whatever _gcd_import returned, which is AFAIU always the "rightmost" module, that is in foo.bar it would be bar and in ., .. or ..baz it would be the rightmost oft whatever these resolve to given the respective package.

Then in case there is no fromlist, we have that if.

  1. if level == 0, i.e. module is absolute, so it returns _gcd_import(name.partition('.')[0]), i.e. the left-most / top-level module. (btw: why does it re-import and not just take it from `sys.modules like it's down in the 3rd case??)
  2. elif not name:, i.e. when name is empty, which it would be if the module to be imported is dots only like ., .. (but not .foo).
    Now here the behaviour seems to differ compared to absolute modules... we really get the current module i.e. the rightmost one (which is not the top-level)
  3. else:, this is for the cases of relative modules that are not only dots, so .foo, ..bar etc.. Here it again returns the top level module (but this time, simply taking it from sys.modules (unlike above where it seemed to "re-import" the top-level.

So we have now two different behaviour for relative modules, which also differs from what might be taken as general rule of thumb ("no fromlist => top level") from the docs.

I think it might be worth to explain that a bit more.

Oh and what might also make sense to document is, that name can be empty (if level >0) and what that means (i.e. ., .., etc.)...

Cheers,
Chris.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    Status

    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions