Not-so-common uses of ‘import’

As they say, Python is a language that does not force you to do things. Instead, it assumes the programmer to be a consenting adult who, knowingly or unknowingly has the power to bend and mold existing paradigms to fit whatever task is at hand. While this easy-peasy mindset sometimes can be an absolute asset, it will inevitably (hopefully not more than once) lead you down the winding path of destruction. Personally I feel that nothing in Python is quite as capable at dragging people down like the “import” statement.

Standard Imports:

import os
from os.path import isfile as isFile
from time import *

Hopefully this is not the first time you’ve heard this, but you should avoid the asterisk on imports. Unless you own the import (like a file with a bunch of common functions in it) then you can never be sure that a function defined within the import has not just blown away a function with the same name in your application.

User-specific Imports:
But what about the following… (Linux only I believe)

import os
import sys
sys.path.append(os.path.join(os.environ['HOME'], 'scripts'))
import userScript

This can be really helpful to provide some user-specific functionality. Basically the username determines the home directory, and the home directory then provides you with a specific subdirectory of Python scripts. If you log in as “root” you will get one set of functionality, whereas if you log in as “user” you will get a different set of functionality.

Watch out though, as environmental variables are very inaccurate. Anybody and anything can change these variables, and can leave you depending on incorrect or invalid values. One of the easiest ways to see this is by having a proper-daemon C/C++ application that calls a Python script. When you log on as any user and execute the binary, you will see the expected results. If you (via socket or RPC or whatever) call it remotely, the user defaults to the system, which unfortunately does not have certain environmental variables (like a “HOME” directory)… in other words, remote call = failure.

Application-relative (not path-relative) Imports:
Here is another one that I have seen, but have rarely used. Similarly to the previous condition, it requires appending to the system path, but this time we are working with a relative directory.

import os
import sys
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
import someClass

There are two special things happening here. First, we are extracting the direct relative path to the file containing this code. If we were to run this through the interpreter, we would get an error because there is no “__file__”. Second, because “__file__” is always the relative path to the Python file, it doesn’t matter where you execute from, absolute path or not, you will always be referring to “..” from the Python file. This is EXTREMELY useful if you need to create an application that can be run via both relative and absolute path.

Path-agnostic Import Best Practices:
Although I have given multiple examples of how to get the system path to not care where you are executing from, I have for the most part found that the best practice with import paths is to use a configuration file. Why did I just complicate the import process even more? Well, lets take a look at the code:

import os
import sys
lines = open(os.path.join(os.path.dirname(__file__), "config.cfg")).splitlines()
paths = [line.split("=")[1] for line in lines if "path" is line.split("=")[0]]
for path in paths: sys.path.append(path)
import customModule

The benefit here is that now when you want to swap out “customModule” with a different version for testing, you dont need to move any directories, or rename any imports. All you do is change the config file. And yes, I did go a little overboard here, and allow you to add as many paths to the system path list as you want. The other big benefit here is that since you are always specifying an absolute path in the configuration file, you are always guaranteed that the import will succeed.