Search code examples
pythonimportrelative-importgoogle-style-guide

Google Python Style Guide & relative imports


Google Python Style Guide says:

Do not use relative names in imports. Even if the module is in the same package, use the full package name. This helps prevent unintentionally importing a package twice.

What would be a sample setup that incurs importing a package twice?


Solution

  • This is a reference to the Python 2 behavior (deprecated since 2.6) of implicit relative imports: allowing import bar in a module in a package foo to refer to the module foo.bar. Consider a directory on sys.path that looks like

    …
    |-- client.py
    `-- pkg
        |-- __init__.py
        |-- mod.py
        `-- script.py
    

    with files having the following contents:

    client.py

    print "client..."
    from pkg import mod,script
    print "client!"
    

    pkg/__init__.py

    print "pkg"
    

    pkg/mod.py

    print "mod: %r"%__name__
    

    pkg/script.py

    print "script:",__name__,__package__
    
    if __name__=='__main__':
      import mod,client
      print "script!"
    

    In this setup mod can easily be imported twice:

    $ PYTHONPATH=… python …/pkg/script.py
    script: __main__ None
    mod: 'mod'
    client...
    pkg
    mod: 'pkg.mod'
    script: pkg.script None
    client!
    script!
    

    In an attempt to reduce configuration overhead, Python adds the directory pkg to sys.path, effectively presuming that script.py is a top-level module script. Unfortunately, that means that import mod creates a top-level module named mod, and the explicit import of pkg.mod later causes another copy of it to exist with its full name (just after importing pkg itself).

    It was recognized that this poses a problem, and later -m was adjusted to tell the module being executed about the package in which it was found so that relative imports (implicit or explicit) work properly:

    $ PYTHONPATH=… python -m pkg.script
    pkg
    script: __main__ pkg
    mod: 'pkg.mod'
    client...
    script: pkg.script None
    client!
    script!
    

    Note that pkg is now imported first (by -m itself!), that script now has a __package__ attribute immediately afterwards, and that mod is imported just once. Of course, script itself is (still) loaded twice, since its name gets replaced with __main__ the first time so that from pkg import script finds it under a different name.

    The moral is that implementing "a module can also be a script" in terms of __name__=='__main__' is fundamentally broken (and replacements have been rejected): a module already has a __name__, and creating a separate module object to be the entry point so that its __name__ can differ is as absurd as copying the Java class (and all its static data) that provides main. Making a module that no one ever imports works, but is oxymoronic (and breaks code inspection that imports all members of a package).