3

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?

Danylo Mysak
  • 1,514
  • 2
  • 16
  • 22
  • Maybe it’s a vague reference to the issues with `__main__`? – Davis Herring Jan 12 '22 at 15:13
  • @DavisHerring What issues are you referring to? – Danylo Mysak Jan 12 '22 at 17:06
  • 1
    `python -m` is very broken in that, to support the silly `__name__` check used to cram an entry point into a module, it runs a **copy** of the module that can end up importing the original. (Using `python -m` to run a *package* mostly avoids these issues.) That doesn’t sound quite the same as what Google is saying here (which is why this isn’t an answer), but it’s the closest match I know. – Davis Herring Jan 13 '22 at 01:45
  • That does sound like a kind of thing this could be about. Could you point me to where this is documented though? I was able to reproduce contents of a module seemingly executing twice, but there appears to be no difference in the behavior of absolute vs. relative imports. For example, `print('A', __name__); import b` in `a.py` and `print('B'); import a` in `b.py`, then `python a.py` results in `A __main__`, `B`, `A a`. – Danylo Mysak Jan 13 '22 at 07:24

1 Answers1

4

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).

Davis Herring
  • 36,443
  • 4
  • 48
  • 76