from __future__ import absolute_import


I'm currently involved in porting a Python 2 codebase into Python 3.  This effort has been going on for a while with several programmers participating. Instead of just jumping in, I decided to review the work already done so that I could get a feel for how the process worked and what kind of problems I'd likely encounter on the way.

I noticed this line at the top many files:
    from __future__ import absolute_import
Hereafter, I refer to the line "from __future__ import absolute_import"  as "the absolute_import line" 

I'd seen this import before and I had a vague understanding for what it did. Never wanting to introduce code into a system that I do not fully comprehend, I decided that I would research it.  Many people have written about Python's import system and I found many outdated posts and conflicting information.  Even PEP 328 that defines the absolute_import line behavior is not entirely clear.  I eventually resorted to spelunking in the Python 2.5 and 2.7 source code to see how it worked.

tl;dr: Importing absolute_import from the __future__ module changes the behavior of implicit relative imports within a Python 2 program.  Since Python 3 disallows that type of import, they must be eliminated from code that is to run under both versions of Python.  The absolute_import line helps find implicit relative imports but once found and converted to absolute imports or explicit relative imports, the absolute_import line serves no further purpose. In a rare case, leaving the absolute_import line in Python 2 code can result in masking the unintended import of the wrong module.*

Some definitions before I go on:

absolute import - an import with unambiguous completely qualified names for all packages and modules:
    import numpy.fft
    from itertools import permutations

implicit relative import - When writing code that is distributed in a package of modules, this is a short cut technique where code within a module can import sister modules or symbols within sister modules without reference to the parent package.  Python 3 does not support this type of import.
    import sister_module_1
    from sister_module_2 import some_obscure_symbol

explicit relative import - introduced in Python 2.5, this replacement syntax employs Unix filesystem-like dot characters to refer to parent packages or modules.  This type of import is always of the form: from <location> import <symbol list>.
    from . import sister_module_1
    from .sister_module_2 import some_obscure_symbol
    from .. import aunt_module_3 
    from ..uncle_package_4 import cousin_module_5

Both Python 2 and Python 3 follow their own strategies when attempting to resolve package and module imports.  Python 2 will first search any containing package to resolve a module name before moving on to searching the sys.path list.  Python 3 does not search the containing package unless explicitly directed to do so through the use of the relative dot syntax.

Using the absolute_import line in Python 2 changes the search strategy to mimic the Python 3 search strategy.  It stops Python 2 from searching the containing package unless the relative dot syntax is used.  I cannot find that an absolute_import line within Python 3 code has any effects at all.

If an absolute_import line is present in a Python 2 file, an implicit relative import will usually result in raising a runtime ImportError.  This means that an absolute_import line will help find instances of implicit relative import while running tests.  By itself, the absolute_import line does not correct the problem, it only points it out*.

To make the same code work in both Python 2 and Python 3, implicit relative imports must be converted to either absolute imports or explicit relative imports.  Once the absolute_import line has been employed to detect implicit relative imports and the implicit relative imports have been converted, the absolute_import line serves no further purpose.

I advocate for the absolute_import line to be used as a temporary tool that should be removed from production code.  In code that has been stripped of implicit relative imports, it is just noise.

Further Reading:


* herein resides the rare special case: if an absolute_import line is present in Python 2 code and it happens that an implicit relative import module name is the same name as a system level package, then the wrong module will be silently imported.  This will likely result in AttributeErrors when using the module, but if the intent of the relative module was to mimic the system level module interface with different behavior, the erroneous import may go undetected.  Only thorough testing and careful inspection can find this sort of problem.