Friday, August 5, 2011

Note to self: .pth

I think I finally got .pth files sorted out. I first ran into this with easy_install (here). The Python library docs are here.

A path configuration file is a file whose name has the form package.pth and exists in one of the four directories mentioned above; its contents are additional items (one per line) to be added to sys.path. Non-existing items are never added to sys.path, but no check is made that the item refers to a directory (rather than a file). No item is added to sys.path more than once. Blank lines and lines beginning with # are skipped. Lines starting with import (followed by space or tab) are executed.

The four directories are:

sys.prefix + lib/site-packages
sys.prefix + lib/site-python
sys.exec_prefix + lib/site-packages
sys.exec_prefix + lib/site-python


So, not all directories on the path are searched for .pth files. I made a nested directory:

/Users/telliott/Desktop/temp/x

and put a .pth file in site-packages:

> cat /Library/Python/2.6/site-packages/x.pth
/Users/telliott/Desktop/temp/x

To test it:

> python -c 'import sys;  L = [p for p in sys.path if "Desk" in p];  print "\n".join(L)'
/Users/telliott/Desktop/temp/x



Of course, I could have just added a path to PYTHONPATH in ~/.bash_profile:

export PYTHONPATH=$PYTHONPATH:$HOME/Desktop/temp



> python -c 'import sys;  L = [p for p in sys.path if "Desk" in p];  print "\n".join(L)'
/Users/telliott/Desktop/temp
/Users/telliott/Desktop/temp/x

You need to restart Terminal to see the effect of changes to PYTHONPATH.


It's worth emphasizing (as pointed out in the quote) that .pth files can contain code. Here is easy_install.pth:

> cat /Library/Python/2.6/site-packages/easy-install.pth
import sys; sys.__plen = len(sys.path)
./Sphinx-0.6.3-py2.6.egg
./docutils-0.6-py2.6.egg
./Jinja2-2.2.1-py2.6.egg
./Pygments-1.1.1-py2.6.egg
./rpy2-2.1.7_20101104-py2.6-macosx-10.6-universal.egg
./numpy-1.5.0-py2.6-macosx-10.6-universal.egg
./Cython-0.14-py2.6-macosx-10.6-universal.egg
./PIL-1.1.7-py2.6-macosx-10.6-universal.egg
import sys; new=sys.path[sys.__plen:]; del sys.path[sys.__plen:]; p=getattr(sys,'__egginsert',0); sys.path[p:p]=new; sys.__egginsert = p+len(new)

It seems roundabout, but easy_install.pth is first adding these paths to sys.path, and then moving them to the position in the path specified by sys.__egginsert.