What does __future__ import absolute_import actually make?

My answer was about absolute import in Python, which I thought I understood based on reading the Python 2.5 change log and the accompanying PEP . However, when installing Python 2.5 and trying to create an example of the correct use of from __future__ import absolute_import , I understand that everything is not so clear.

Directly from the above list of changes, this statement accurately summarized my understanding of the absolute import change:

Say you have a package directory as follows:

 pkg/ pkg/__init__.py pkg/main.py pkg/string.py 

Defines a package named pkg containing the submodules pkg.main and pkg.string .

Consider the code in the main.py module. What happens if it executes the import string statement? In Python 2.4 and earlier, it will first search the package directory for relative imports, find pkg / string.py, import the contents of this file as the pkg.string module, and this module is bound to the name "string" in the pkg.main module namespace pkg.main

So, I created this exact directory structure:

 $ ls -R .: pkg/ ./pkg: __init__.py main.py string.py 

__init__.py and string.py are empty. main.py contains the following code:

 import string print string.ascii_uppercase 

As expected, doing this with Python 2.5 completes with an AttributeError :

 $ python2.5 pkg/main.py Traceback (most recent call last): File "pkg/main.py", line 2, in <module> print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' 

However, further in 2.5 changelog, we find this (emphasized by me):

In Python 2.5, you can switch import behavior to absolute import using the from __future__ import absolute_import directive. This absolute import behavior will default in a future version (possibly Python 2.7). When absolute imports are by default, import string will always look for a standard library version.

Thus, I created pkg/main2.py identical to main.py , but with an additional directive for future imports. Now it looks like this:

 from __future__ import absolute_import import string print string.ascii_uppercase 

Running this with Python 2.5, however ... using AttributeError :

 $ python2.5 pkg/main2.py Traceback (most recent call last): File "pkg/main2.py", line 3, in <module> print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' 

This is quite categorically contrary to the assertion that import string will always find the version of std-lib with absolute import enabled. What else, despite the warning that absolute imports will become โ€œnew by defaultโ€, I ran into this problem using both Python 2.7 and without the __future__ directive:

 $ python2.7 pkg/main.py Traceback (most recent call last): File "pkg/main.py", line 2, in <module> print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' $ python2.7 pkg/main2.py Traceback (most recent call last): File "pkg/main2.py", line 3, in <module> print string.ascii_uppercase AttributeError: 'module' object has no attribute 'ascii_uppercase' 

as well as Python 3.5 with or without (provided that the print statement changes in both files):

 $ python3.5 pkg/main.py Traceback (most recent call last): File "pkg/main.py", line 2, in <module> print(string.ascii_uppercase) AttributeError: module 'string' has no attribute 'ascii_uppercase' $ python3.5 pkg/main2.py Traceback (most recent call last): File "pkg/main2.py", line 3, in <module> print(string.ascii_uppercase) AttributeError: module 'string' has no attribute 'ascii_uppercase' 



I tested other variations of this. Instead of string.py I created an empty module - a directory called string containing only empty __init__.py - and instead of issuing import from main.py , I have cd 'd to pkg and start importing directly from REPL. None of these options (as well as their combination) changed the results above. I canโ€™t come to terms with what I read about the __future__ directive and absolute import.

It seems to me that this is easily explained by the following (this is from Python 2 docs, but this statement remains unchanged in the same docs for Python 3):

sys.path

(...)

As initialized at program startup, the first element of this list, path[0] , is the directory containing the script that was used to invoke the Python interpreter. If the script directory is not accessible (for example, if the interpreter is invoked interactively or if the script is read from standard input), path[0] is an empty string that directs Python to search for modules in the current one first.

So what am I missing? Why __future__ operator apparently not do what it says, and what is the resolution of this contradiction between the two sections of the documentation, as well as between the described and actual behavior?

+135
python python-import
Nov 16 '15 at 20:18
source share
2 answers

The line of changes is incorrectly formulated. from __future__ import absolute_import does not care about whether something is part of the standard library, and import string will not always give you a standard library module with absolute import.

from __future__ import absolute_import means that if you import string , Python will always look for the top-level module string , not current_package.string . However, this does not affect the logic used by Python to determine which file is the string module. When you do

 python pkg/script.py 

pkg/script.py not like part of the package in Python. Following the usual procedures, the pkg directory is added to the path, and all .py files in the pkg directory look like top-level modules. import string finds pkg/string.py not because it makes relative imports, but because pkg/string.py is represented by the top-level module string . The fact that this is not a standard string module does not appear.

To run the file as part of the pkg package, you could do

 python -m pkg.script 

In this case, the pkg directory will not be added to the path. However, the current directory will be added to the path.

You can also add some template for pkg/script.py so that Python treats it as part of the pkg package even when run as a file:

 if __name__ == '__main__' and __package__ is None: __package__ = 'pkg' 

However, this will not affect sys.path . You will need additional processing to remove the pkg directory from the path, and if the pkg parent directory is not in the path, you will also need to stick to this path.

+88
Nov 16 '15 at 20:35
source share

The difference between absolute and relative imports comes into play only when you import a module from a package, and this module imports another submodule from this package. See the difference:

 $ mkdir pkg $ touch pkg/__init__.py $ touch pkg/string.py $ echo 'import string;print(string.ascii_uppercase)' > pkg/main1.py $ python2 Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import pkg.main1 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "pkg/main1.py", line 1, in <module> import string;print(string.ascii_uppercase) AttributeError: 'module' object has no attribute 'ascii_uppercase' >>> $ echo 'from __future__ import absolute_import;import string;print(string.ascii_uppercase)' > pkg/main2.py $ python2 Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import pkg.main2 ABCDEFGHIJKLMNOPQRSTUVWXYZ >>> 

In particular:

 $ python2 pkg/main2.py Traceback (most recent call last): File "pkg/main2.py", line 1, in <module> from __future__ import absolute_import;import string;print(string.ascii_uppercase) AttributeError: 'module' object has no attribute 'ascii_uppercase' $ python2 Python 2.7.9 (default, Dec 13 2014, 18:02:08) [GCC] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import pkg.main2 ABCDEFGHIJKLMNOPQRSTUVWXYZ >>> $ python2 -m pkg.main2 ABCDEFGHIJKLMNOPQRSTUVWXYZ 

Note that python2 pkg/main2.py has a different behavior, then it starts python2 and then imports pkg.main2 (which is equivalent to using the -m switch).

If you want to run a package submodule, always use the -m switch, which prevents the interpreter from sorting through the sys.path list and correctly processes the semantics of the submodule.

In addition, I prefer to use explicit relative imports for package submodules, as they provide more semantics and improve error messages in the event of a failure.

+43
Nov 16 '15 at 20:34
source share



All Articles