19 02
2018

Mac OS X framework Python and virtual environments

While Python works generally fine on OS X, there are some gotchas. This particular one has basically kept me from using matplotlib and lead me to resort to doing my data visualization in R. This is not a problem in itself, but it limits the visualizations to be static if one doesn't want to go deeper into R programming.

(env) $ python3.6
Python 3.6.4 (default, Dec 21 2017, 20:33:21)
[GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import matplotlib.pyplot as plt
Traceback (most recent call last):
File "", line 1, in 
File ".../env/lib/python3.6/site-packages/matplotlib/pyplot.py", line 116, in 
  _backend_mod, new_figure_manager, draw_if_interactive, _show = pylab_setup()
File ".../env/lib/python3.6/site-packages/matplotlib/backends/__init__.py", line 60, in pylab_setup
  [backend_name], 0)
File ".../env/lib/python3.6/site-packages/matplotlib/backends/backend_macosx.py", line 17, in 
  from matplotlib.backends import _macosx
RuntimeError: Python is not installed as a framework. The Mac OS X backend
will not be able to function correctly if Python is not installed as a
framework. See the Python documentation for more information on installing
Python as a framework on Mac OS X. Please either reinstall Python as a
framework, or try one of the other backends. If you are using (Ana)Conda
please install python.app and replace the use of 'python' with 'pythonw'. See
'Working with Matplotlib on OSX' in the Matplotlib FAQ for more information.

The essence of the problem is that for some reason one cannot use certain graphics/UI libraries from a "non framework Python". I don't know what "framework Python" really means, but it seems that when one creates a virtualenv using any Python build (including a framework one), it ceases to be a "framework Python" build. The internet is generally full of poor advice on this subject. Starting from suggestions to install Python from a .pkg provided at python.org, to global installations of libraries, and to chaotic symlinking of things.

I personally use MacPorts and don't intend to start building or installing Python interpreters by hand if I really don't need to do so. Luckily, MacPorts works fine and installs as a "framework Python". One just has to use it the right way. The solution was found from the official documentation, but it was still painful to figure out as the concept of "framework Python" is murky and it isn't all that clear as to which builds are framework and not. I wasn't sure MacPorts installed a "framework build" before I was able to make matplotlib actually work. This led me astray, to suspect the problem is in the Python build, and not in the way I was using it.

To fix the problem, one has to use the actual binary from the framework build Python, i.e. the wrapper created by virtualenv within the environment's bin directory doesn't work.

(env) $ /opt/local/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6 Python 3.6.4 (default, Dec 21 2017, 20:33:21) [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> import matplotlib.pyplot as plt >>>

However, this is problematic as the Python interpreter doesn't use the virtual environment - again, installing libraries globally is not a real option. Luckily, this can be fixed by setting up the PYTHONHOME environment variable accordingly. To make moving from one project to another with their own virtualenvs, one can set up a shell function.

PY36BUILD=/opt/local/Library/Frameworks/Python.framework/Versions/3.6
PY36BIN=$PY36BUILD/bin/python3.6
PY3BIN=$PY36BIN
export PATH=$PY3BIN:$PATH

function frameworkpython {
  if [[ ! -z "$VIRTUAL_ENV" ]]; then
    PYTHONHOME=$VIRTUAL_ENV $PY3BIN "$@"
  else
    $PY3BIN "$@"
  fi
}

The above snippet creates a shell-callable function frameworkpython which can be run when a virtualenv is activated and it correctly sets up the PYTHONHOME environment variable so that libraries installed in the virtual environment are used.