3.8. Shell Environment Variables
Shell variables, sometimes known as environment variables, are made available to Python scripts as os.environ, a Python dictionary-like object with one entry per variable setting in the shell. Shell variables live outside the Python system; they are often set at your system prompt or within startup files and typically serve as system-wide configuration inputs to programs.
In fact, by now you should be familiar with a prime example: the PYTHONPATH module search path setting is a shell variable used by Python to import modules. By setting it once in your system startup files, its value is available every time a Python program is run. Shell variables can also be set by programs to serve as inputs to other programs in an application; because their values are normally inherited by spawned programs, they can be used as a simple form of interprocess communication.
3.8.1. Fetching Shell Variables
In Python, the surrounding shell environment becomes a simple preset object, not special syntax. Indexing os.environ by the desired shell variable's name string (e.g., os.environ['USER']) is the moral equivalent of adding a dollar sign before a variable name in most Unix shells (e.g., $USER), using surrounding percent signs on DOS (%USER%), and calling getenv("USER") in a C program. Let's start up an interactive session to experiment:
>>> import os >>> os.environ.keys( ) ['WINBOOTDIR', 'PATH', 'USER', 'PP2HOME', 'CMDLINE', 'PYTHONPATH', 'BL*ER', 'X', 'TEMP', 'COMSPEC', 'PROMPT', 'WINDIR', 'TMP'] >>> os.environ['TEMP'] 'C:\\windows\\TEMP'
Here, the keys method returns a list of set variables, and indexing fetches the value of the shell variable TEMP on Windows. This works the same way on Linux, but other variables are generally preset when Python starts up. Since we know about PYTHONPATH, let's peek at its setting within Python to verify its content (as I wrote this, mine was set to the roots of the book examples trees for the third and second editions):
>>> os.environ['PYTHONPATH'] 'C:\\Mark\\PP3E-cd\\Examples;C:\\Mark\\PP2E-cd\\Examples' >>> >>> for dir in os.environ['PYTHONPATH'].split(os.pathsep): ... print dir ... C:\Mark\PP3E-cd\Examples C:\Mark\PP2E-cd\Examples
PYTHONPATH is a string of directory paths separated by whatever character is used to separate items in such paths on your platform (e.g., ; on DOS/Window, : on Unix and Linux). To split it into its components, we pass to the split string method an os.pathsep delimitera portable setting that gives the proper separator for the underlying machine.
3.8.2. Changing Shell Variables
Like normal dictionaries, the os.environ object supports both key indexing and assignment. As usual, assignments change the value of the key:
>>> os.environ['TEMP'] = r'c:\temp' >>> os.environ['TEMP'] 'c:\\temp'
But something extra happens here. In recent Python releases, values assigned to os.environ keys in this fashion are automatically exported to other parts of the application. That is, key assignments change both the os.environ object in the Python program as well as the associated variable in the enclosing shell environment of the running program's process. Its new value becomes visible to the Python program, all linked-in C modules, and any programs spawned by the Python process.
Internally, key assignments to os.environ call os.putenva function that changes the shell variable outside the boundaries of the Python interpreter. To demonstrate how this works, we need a couple of scripts that set and fetch shell variables; the first is shown in Example 3-4.
Example 3-4. PP3E\System\Environment\setenv.py
This setenv.py script simply changes a shell variable, USER, and spawns another script that echoes this variable's value, as shown in Example 3-5.
Example 3-5. PP3E\System\Environment\echoenv.py
No matter how we run echoenv.py, it displays the value of USER in the enclosing shell; when run from the command line, this value comes from whatever we've set the variable to in the shell itself:
C:\...\PP3E\System\Environment>set USER=Bob C:\...\PP3E\System\Environment>python echoenv.py echoenv... Hello, Bob
When spawned by another script such as setenv.py, though, echoenv.py gets whatever USER settings its parent program has made:
C:\...\PP3E\System\Environment>python setenv.py setenv... Bob echoenv... Hello, Brian echoenv... Hello, Arthur ?Gumby echoenv... Hello, Gumby C:\...\PP3E\System\Environment>echo %USER% Bob
This works the same way on Linux. In general terms, a spawned program always inherits environment settings from its parents. Spawned programs are programs started with Python tools such as os.spawnv on Windows, the os.fork/exec combination on Unix and Linux, and os.popen and os.system on a variety of platforms. All programs thus launched get the environment variable settings that exist in the parent at launch time.[*]
From a larger perspective, setting shell variables like this before starting a new program is one way to pass information into the new program. For instance, a Python configuration script might tailor the PYTHONPATH variable to include custom directories just before launching another Python script; the launched script will have the custom search path because shell variables are passed down to children (in fact, watch for such a launcher script to appear at the end of Chapter 6).
3.8.3. Shell Variable Details
Notice the last command in the preceding examplethe USER variable is back to its original value after the top-level Python program exits. Assignments to os.environ keys are passed outside the interpreter and down the spawned programs chain, but never back up to parent program processes (including the system shell). This is also true in C programs that use the putenv library call, and it isn't a Python limitation per se.
It's also likely to be a nonissue if a Python script is at the top of your application. But keep in mind that shell settings made within a program usually endure only for that program's run and for the run of its spawned children. If you need to export a shell variable setting so that it lives on after Python exits, you may be able to find platform-specific extensions that do this; search http://www.python.org or the Web at large.
Another subtlety: currently, changes to os.environ automatically call os.putenv, which runs the putenv call in the C library if the later is available on your platform; this exports the setting outside Python to any linked-in C code too. However, direct calls to os.putenv do not update os.environ to reflect the change, so os.environ changes are preferred.
Also note that environment settings are loaded into os.environ on startup and not on each fetch; hence, changes made by linked-in C code after startup may not be reflected in os.environ. Python does have an os.getenv call today, but it is translated into an os.environ fetch on most platforms, not into a call to getenv in the C library. Most applications won't need to care, especially if they are pure Python code. On platforms without a putenv call, os.environ can be passed as a parameter to program startup tools to set the spawned program's environment.