[Import-SIG] Eliminating implicit __main__ relative imports

Nick Coghlan ncoghlan at gmail.com
Tue Mar 28 06:25:06 EDT 2017


Hi folks,

http://bugs.python.org/issue29929 covers an idea I had today that may
help us finally resolve the name shadowing problem, where folks
inadvertently import their __main__ script as a module because it
happens to shadow the name of a standard library or third party module
that they or one of their dependencies is trying to import.

The gist of the idea is to ask what if, instead of doing:

    sys.path.insert(0, <the script directory>)

we instead did this little dance to add an anonymous top-level
main-relative namespace package:

    mod = mod.ModuleType("")
    mod.__path__ = [<the script directory>]
    sys.modules[""] = mod
    __main__.__package__ = ""

That's already enough to allow explicit relative imports via "mod =
importlib.import_module('name', package='')", but a couple of sanity
checks elsewhere in importlib guarding against empty module names
would need to be relaxed to allow the "from . import name" syntax.

sys.path would then only be modified when:

- you used the -m switch (current directory inserted as sys.path[0])
- you executed a sys.path entry (the entry inserted as sys.path[0])
- you modified it explicitly

For the other forms of __main__ invocation:

- the implicit main-relative namespace would be created when a
top-level module was executed with "-m" (including when running
__main__ from a sys.path entry)
- it would *not* be created when a package or submodule was executed
with "-m" (as in that case, there's already a real package to anchor
any explicitly relative imports)

Thanks to PEP 366, cross-version compatible main-relative imports
would then look like:

    if __package__ is not None:
        from . import relative_module_name
    else:
        import relative_module_name

As a transition plan, a deprecation warning could be emitted for the
latter form by:

1. In 3.7, populating a private sys._main_path_entry in the sys module
in addition to including it in both __main__.__path__ and sys.path

2. In 3.7, emit a warning when an import is satisfied from the
sys._main_path_entry directory and the fully qualified module name
*doesn't* start with "." (i.e. an empty parent package)

3. In 3.8, stop populating sys.path[0] with the script directory, stop
setting sys._main_path_entry, and stop emitting the deprecation
warning

I'm posting the idea here first to give folks a change to poke
technical holes in it before I start trying to formulate it into a PEP
for python-ideas and python-dev.

Cheers.
Nick.

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


More information about the Import-SIG mailing list