pyenv & virtualenv can get along

In my (apparently) continuing list of tiny hassles with PyEnv, I finally figured out how to “fix” the PyEnv notion of a virtualenv. This may apply only to my setup: my main python version is managed by homebrew.

If you install a new point release of python via homebrew (e.g. 2.7.11 atop 2.7.9), and then run brew cleanup, you’ll find that your virtualenvs created with 2.7.9 may no longer function correctly.

There are actually 2 problems here:
  1. The pyenv virtualenv may be pointing to a (now) non-existent python library
  2. The original python libraries may still be referenced in sys.path

The first was easy to find - for python 2 virtualenvs managed by pyenv, a file .Python is symlinked to the corresponding framework version. The frameworks are only identified by the major & minor versions (e.g. ‘2.7’), but the file system path to the homebrew installed version contains the point release value as well. So, all of those links need to be recreated. E.g.:

cd ~/.pyenv/versions/py2_venv_name
rm .Python && ln -s /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/Python .Python

The second was more confusing. After fixing the first, the virtualenv’s python would load, but would report errors like “ImportError: No module named __future__”. That needs to be fixed with a similar change to the virtualenv’s lib/python2.7/orig-prefix.txt file, which is consumed by lib/python2.7/site.py. It also contains a file path to the framework version of python. So:

cd ~/.pyenv/versions/py2_venv_name/lib/python2.7
new_framework=$(readlink ../../.Python)
new_framework=${new_framework%/Python}
echo -n $new_framework >orig-prefix.txt
rm site.pyc
../../bin/python -c 'import __future__' || echo "still broken"

N.B. that the file does not have a final newline character, hence the bash gymnastics above.