Discovering Pyenv

Before learning Python, I had been working with Ruby over a period of 3 years having the opportunity to participate in a great Meetup Group as well my professional role as a Development Operations Engineer at LivingSocial.

I soon discovered Sam Stephenson’s GitHub project Rbenv.

Sam is an A+ contributor in my book and has developed other great projects such as the Bash unit test framework BATS.

Rbenv hooks into your interactive shell environment by manipulating PATH and using shims which are a set of shell scripts and functions which intercept the invocation of executables such as ruby gem, or whatever a package installs as an executable, (e.g. rake).

With these shims, Rbenv can conditionaly execute different versions of Ruby based on the shell environment or by consulting the file .ruby-version which can set this based on a current directory path.

This allows you to install many versions of Ruby and easily isolate the version to a specific project’s code base.

This is a great pattern (as well as utilizing Bundler) for limiting a majority of the side-effects encountered when developing or deploying multiple applications in the same execution environment.

I was perusing through a friend of mine’s Github account recently and stumbled upon a Python version of Rbenv entitled Pyenv from Yuu Yamashita.

Knowing the power of Rbenv I was excited to give this version a try.

Setting up a Build Environment

Since Pyenv builds many of it’s versions of python from source, its important to have a compiler as well as associated build tools and libraries installed previous to setting it up in your shell.

This article does not address the MacOS but you will likely need to install Xcode from Apple App Store (free) which is their compiler and associated tools.

I ran the following script on a Fedora 20 Linux desktop to install the tools and libraries needed for a basic build environment (your mileage may vary):

~/ (run as root or via sudo):

#!/usr/bin/env bash

cd /var/tmp

packages="gcc gcc-c++ tar binutils readline-devel sqlite-devel zlib-devel \
  openssl-devel expat-devel postgresql-devel mysql-devel texinfo curl git"

yum -y install ${packages} && yum clean all


for url in ${yaml_url} ${ffi_url}; do
  extract_dir="$(basename $url .tar.gz)"
  curl -sL $url | tar zxf -
  cd ${extract_dir} && ./configure && make install
  cd .. && rm -r ${extract_dir}

Setting up Pyenv

Configure the shell

The official installation instructions are here however I wrote the following snippet which initiates an install if Pyenv does not exist.

In addition there is an optional cleanup function which strips any references from the environment to cleanly initialize new Pyenv settings or location.

Add to /.bash_profile

pyenv_cleanup() {
  export PATH=$(echo $PATH \
    | perl -p -e "s{(^|:)${PYENV_ROOT:-$HOME/.pyenv}[^:]*}{}g; s{^:}{};")
  for v in ${!PYENV_*}; do unset $v; done

pyenv_init() {
  export PYENV_ROOT="${PYENV_ROOT:-$HOME/.pyenv}"
  local pyenv="git://"
  local pyenv_virtualenv=""

  [[ -d ${PYENV_ROOT} ]] || git clone ${pyenv} ${PYENV_ROOT}
  [[ -d ${PYENV_ROOT}/cache ]] || mkdir  ${PYENV_ROOT}/cache

  [[ -d ${PYENV_ROOT}/plugins/pyenv-virtualenv ]] \
    || git clone ${pyenv_virtualenv} ${PYENV_ROOT}/plugins/pyenv-virtualenv

  export PATH="${PYENV_ROOT}/bin:$PATH"
  eval "$(pyenv init -)"
  eval "$(pyenv virtualenv-init -)"


Source the environment

user@sys:~$ source ~/.bash_profile

Install Python

Use pyenv install --list to list all versions available for install.

In this example we will install python 2.7.8.

user@sys:~$ pyenv install 2.7.8
  Downloading Python-2.7.8.tgz...
  Installing Python-2.7.8...
  Installing setuptools from
  Installing pip from
  Installed Python-2.7.8 to /home/u/.pyenv/versions/2.7.8

Enter the project directory and create a Virtualenv

user@sys:~$ mkdir -p ~/src/myproject && cd ~/src/myproject

Create a virtualenv by running pyenv <python_version> <virtualenv_name>.

In this example we use command line substitution to automatically set the virtualenv name to the project’s root directory.

user@sys:~/src/myproject$ pyenv virtualenv 2.7.8 $(basename $(pwd))
  New python executable in /home/u/.pyenv/versions/myproject/bin/python
  Installing setuptools, pip...done.
  Installing setuptools from
  Extracting in /tmp/tmpTWQYyY
  Now working in /tmp/tmpTWQYyY/setuptools-7.0
  Installing Setuptools
  running install
  running bdist_egg
  running egg_info
  writing requirements to setuptools.egg-info/requires.txt

To automatically set the virtualenv every time you enter the project directory, use pyenv local <virtualenv> to create a .python-version file.

user@sys:~/src/myproject$ pyenv local myproject
  pyenv-virtualenv: activate myproject

Here are some example command to run to see how virtualenv is set via a .python-version file and how it activates and de-activates as you navigate in and out of the project directory.

user@sys:~/src/myproject$ cat .python-version

user@sys:~/src/myproject$ pyenv version
  myproject (set by /home/u/~/src/myproject/.python-version)

user@sys:~/src/myproject$ cd ~
  pyenv-virtualenv: deactivate myproject

user@sys:~$ cd -
  pyenv-virtualenv: activate myproject

Install any required packages

user@sys:~/src/myproject$ pip install <package...>


user@sys:~/src/myproject$ pip install -r requirements.txt


Share this post