I l@ve RuBoard Previous Section Next Section

2.7 Current Working Directory

The notion of the current working directory (CWD) turns out to be a key concept in some scripts' execution: it's always the implicit place where files processed by the script are assumed to reside, unless their names have absolute directory paths. As we saw earlier, os.getcwd lets a script fetch the CWD name explicitly, and os.chdir allows a script to move to a new CWD.

Keep in mind, though, that filenames without full pathnames map to the CWD, and have nothing to do with your PYTHONPATH setting. Technically, the CWD is always where a script is launched from, not the directory containing the script file. Conversely, imports always first search the directory containing the script, not the CWD (unless the script happens to also be located in the CWD). Since this distinction is subtle and tends to trip up beginners, let's explore it in more detail.

2.7.1 CWD, Files, and Import Paths

When you run a Python script by typing a shell command line like python dir1\dir2\file.py, the CWD is the directory you were in when you typed this command, not dir1\dir2. On the other hand, Python automatically adds the identity of the script's home directory to the front of the module search path, such that file.py can always import other files in dir1\dir2, no matter where it is run from. To illustrate, let's write a simple script to echo both its CWD and module search path:

C:\PP2ndEd\examples\PP2E\System>type whereami.py
import os, sys
print 'my os.getcwd =>', os.getcwd(  )            # show my cwd execution dir
print 'my sys.path  =>', sys.path[:6]           # show first 6 import paths
raw_input(  )                                     # wait for keypress if clicked

Now, running this script in the directory in which it resides sets the CWD as expected, and adds an empty string ('') to the front of the module search path, to designate the CWD (we met the sys.path module search path earlier):

C:\PP2ndEd\examples\PP2E\System>set PYTHONPATH=C:\PP2ndEd\examples
C:\PP2ndEd\examples\PP2E\System>python whereami.py
my os.getcwd => C:\PP2ndEd\examples\PP2E\System
my sys.path  => ['', 'C:\\PP2ndEd\\examples', 'C:\\Program Files\\Python
\\Lib\\plat-win', 'C:\\Program Files\\Python\\Lib', 'C:\\Program Files\\
Python\\DLLs', 'C:\\Program Files\\Python\\Lib\\lib-tk']

But if we run this script from other places, the CWD moves with us (it's the directory where we type commands), and Python adds a directory to the front of the module search path that allows the script to still see files in its own home directory. For instance, when running from one level up (".."), the "System" name added to the front of sys.path will be the first directory Python searches for imports within whereami.py ; it points imports back to the directory containing the script run. Filenames without complete paths, though, will be mapped to the CWD (C:\PP2ndEd\examples\PP2E ), not the System subdirectory nested there:

C:\PP2ndEd\examples\PP2E\System>cd .. 
C:\PP2ndEd\examples\PP2E>python System\whereami.py 
my os.getcwd => C:\PP2ndEd\examples\PP2E
my sys.path  => ['System', 'C:\\PP2ndEd\\examples', ...  rest same...  ]

C:\PP2ndEd\examples\PP2E>cd .. 
C:\PP2ndEd\examples>python PP2E\System\whereami.py 
my os.getcwd => C:\PP2ndEd\examples
my sys.path  => ['PP2E\\System', 'C:\\PP2ndEd\\examples', ...  rest same...  ]

C:\PP2ndEd\examples\PP2E\System>cd PP2E\System\App 
C:\PP2ndEd\examples\PP2E\System\App>python ..\whereami.py 
my os.getcwd => C:\PP2ndEd\examples\PP2E\System\App
my sys.path  => ['..', 'C:\\PP2ndEd\\examples', ...  rest same...  ]

The net effect is that filenames without directory paths in a script will be mapped to the place where the command was typed (os.getcwd), but imports still have access to the directory of the script being run (via the front of sys.path). Finally, when a file is launched by clicking its icon, the CWD is just the directory that contains the clicked file. The following output, for example, appears in a new DOS console box, when whereami.py is double-clicked in Windows explorer:

my os.getcwd => C:\PP2ndEd\examples\PP2E\System
my sys.path  => ['C:\\PP2NDED\\EXAMPLES\\PP2E\\SYSTEM', 'C:\\PP2ndEd\\examples',
'C:\\Program Files\\Python\\Lib\\plat-win', 'C:\\Program Files\\Python\\Lib',
'C:\\Program Files\\Python\\DLLs']

In this case, both the CWD used for filenames and the first import search directory are the directory containing the script file. This all usually works out just as you expect, but there are two pitfalls to avoid:

  • Filenames might need to include complete directory paths if scripts cannot be sure from where they will be run.

  • Command-line scripts cannot use the CWD to gain import visibility to files not in their own directories; instead, use PYTHONPATH settings and package import paths to access modules in other directories.

For example, files in this book can always import other files in their own home directories without package path imports, regardless of how they are run (import filehere) but must go through the PP2E package root to find files anywhere else in the examples tree (from PP2E.dir1.dir2 import filethere) even if they are run from the directory containing the desired external module. As usual for modules, the PP2E\dir1\dir2 directory name could also be added to PYTHONPATH to make filethere visible everywhere without package path imports (though adding more directories to PYTHONPATH increases the likelihood of name clashes). In either case, though, imports are always resolved to the script's home directory or other Python search path settings, not the CWD.

2.7.2 CWD and Command Lines

This distinction between the CWD and import search paths explains why many scripts in this book designed to operate in the current working directory (instead of one whose name is passed in) are run with command lines like this:

C:\temp>python %X%\PyTools\cleanpyc-py.py                   process cwd

In this example, the Python script file itself lives in the directory C:\PP2ndEd\examples\PP2E\PyTools, but because it is run from C:\temp, it processes the files located in C:\temp (i.e., in the CWD, not in the script's home directory). To process files elsewhere with such a script, simply cd to the directory to be processed to change the CWD:

C:\temp>cd C:\PP2nEd\examples 
C:\PP2ndEd\examples>python %X%\PyTools\cleanpyc-py.py       process cwd

Because the CWD is always implied, a cd tells the script which directory to process in no less certain terms that passing a directory name to the script explicitly like this:

C:\...\PP2E\PyTools>python find.py *.py C:\temp            process named dir

In this command line, the CWD is the directory containing the script to be run (notice that the script filename has no directory path prefix); but since this script processes a directory named explicitly on the command line (C:\temp), the CWD is irrelevant. Finally, if we want to run such a script located in some other directory to process files located in some other directory, we can simply give directory paths to both:

C:\temp>python %X%\PyTools\find.py *.cxx C:\PP2ndEd\examples\PP2E

Here, the script has import visibility to files in its PP2E\PyTools home directory and processes files in the PP2E root, but the CWD is something else entirely (C:\temp). This last form is more to type, of course, but watch for a variety of CWD and explicit script-path command lines like these in this book.

Whenever you see a %X% in command lines like those in the preceding examples, it refers to the value of the shell environment variable named X. It's just a shorthand for the full directory pathname of the PP2E book examples package root directory, which I use to point to scripts' files. On my machines, it is preset in my PP2E\Config setup-pp* files like this:

set X=C:\PP2ndEd\examples\PP2E --DOS
setenv X /home/mark/PP2ndEd/examples/PP2E --Unix/csh

That is, it is assigned and expanded to the directory where PP2E lives on the system. See the Config\setup-pp* files for more details, and see later in this chapter for more on shell variables. You can instead type full paths everywhere you see %X% in this book, but your fingers and your keyboard are probably both better off if you set X to your examples root.

    I l@ve RuBoard Previous Section Next Section