Hacker Codex   Linux servers · Python development · macOS tinkering

Set Up Python and Install Django on Mac OS X Lion 10.7

NOTE: This guide was written for Lion 10.7 and has since been superceded by these new guides:

First steps

Lion has done away with the “Sites” folder by default, but it’s easy to add it back — the custom icon will even show up automatically. Use the Finder, or enter the following in a Terminal session:

mkdir ~/Sites

Another change is the hidden ~/Library folder. We can make it visible again with the following command (which gets overridden by OS updates and must be run again afterwards):

chflags nohidden ~/Library/

Since Lion is a full 64-bit system, we’ll save some headaches by letting our compiler know that all compilation should assume 64 bits. Open ~/.bash_profile …

vim ~/.bash_profile

… and add:

# Set architecture flags
export ARCHFLAGS="-arch x86_64"

With those first steps out of the way, now it’s time to get the necessary compilation tools in place.


Installing development-related software in the past has required the compiler tool-chain that comes with Xcode. Thankfully, if you don’t need or want Xcode, those compiler tools are now available separately, saving download time and many gigabytes of disk space. Assuming you have your Apple ID credentials handy, head over to the Developer Downloads area, download the Command Line Tools for Xcode, and install it.

Alternatively, if you need Xcode for Mac/iOS app development or find yourself occasionally using it to manually compile open-source Mac applications, download Xcode from either the Developer Downloads area or the Mac App Store. Make sure you’re on a high-bandwidth connection, because Xcode is a massive beast. Once you’ve installed Xcode, launch the Xcode application, visit the app preferences, find the “Downloads” pane, and download the above-mentioned Command Line Tools from within the Xcode application.


Sometimes you may need cross-platform software — usually without a GUI and accessible only via the command line — that isn’t readily available via the Mac App Store. As someone who used MacPorts for years, I can’t begin to explain the relative awesomeness that is Homebrew. It’s an indispensable tool that should be in every Mac developer’s arsenal, so let’s fire up Terminal.app and install it:

ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/master/Library/Contributions/install_homebrew.rb)"

That’s it — Homebrew is now installed. While it should have the latest version of Homebrew and its formulae, let’s run the update command just in case:

brew update

If the “brew update” command produces an error, make sure /usr/local is owned by you and not by root:

sudo chown $USER /usr/local
brew update

The Homebrew wiki has a full command list and a bunch of other useful information, but here’s a quick command to install some packages that I often find useful:

brew install bash-completion byobu ssh-copy-id wget

You can run “brew info byobu”, for example, if you want to know what those packages do.

Python site-packages

Let’s say you want to install a Python package, such as the fantastic virtualenv environment isolation tool. Nearly every Python-related article for Mac OS X tells the reader to install it via sudo easy_install virtualenv. Here’s why I don’t do it that way:

  1. installs with root permissions
  2. installs into the global /Library instead of the user’s
  3. Distribute” has less bugs than the default legacy easy_install

If I install a Python package and decide I want to delete it, I like having the option to use the Finder to drag it to the Trash without any permission-related complaints. Plus, keeping the Python packages isolated to the user account just makes more sense to me. Last but not least, Distribute is a fork of Setuptools/easy_install that aims to be more reliable with less bugs.

Traditionally, I’ve always used the --user flag to install Python packages into ~/.local, a behavior that was consistent across Macs, Linux, and other UNIX systems. It seems Lion has changed this behavior, however, installing into ~/Library/Python/2.7 when it encounters the --user flag. Since I prefer to have this location remain consistent across the various systems I encounter, I changed the flag from --user to the more specific --prefix=~/.local. Whichever you choose, please keep in mind that the instructions here assume the latter.

With that tangent behind us, let’s install Distribute:

mkdir -p ~/.local/lib/python2.7/site-packages
wget http://pypi.python.org/packages/source/d/distribute/distribute-0.6.28.tar.gz
tar -xzf distribute-0.6.28.tar.gz
cd distribute-0.6.28
python setup.py install --prefix=~/.local

The only disadvantage of installing into ~/.local is that we need to add some locations to the PATH and PYTHONPATH environment variables. Let’s edit our .bash_profile…

vim ~/.bash_profile

… and add a few lines:

# Path ------------------------------------------------------------
if [ -d ~/.local/bin ]; then
  export PATH=~/.local/bin:$PATH

# Python path -----------------------------------------------------
if [ -d ~/.local/lib/python2.7/site-packages ]; then
  export PYTHONPATH=~/.local/lib/python2.7/site-packages:$PYTHONPATH

# Load in .bashrc -------------------------------------------------
if [ -f ~/.bashrc ]; then
  source ~/.bashrc

Let’s load those directives now via:

touch ~/.bashrc
source ~/.bash_profile

Lion’s easy_install is normally located in /usr/bin, so let’s make sure that our local version is the one that’s used by default:

which easy_install

Assuming the response is ~/.local/bin/easy_install, then we’re all set!

virtualenv and virtualenvwrapper

Python packages installed to ~/.local are indeed local to the user, but they are also global in the sense that they are available across all of a given user’s projects. That can be convenient at times, but it also creates problems. For example, sometimes one project needs the latest version of Django, while another needs Django 1.3 to retain compatibility with a critical third-party extension. This is precisely the problem that virtualenv was designed to solve. On my systems, virtualenv, virtualenvwrapper, and Mercurial are the only Python packages that are always available — every other package is confined to its virtual environment.

Let’s install virtualenv and its companion virtualenvwrapper:

easy_install --prefix=~/.local virtualenv virtualenvwrapper

We’ll then open/create the ~/.bashrc file…

vim ~/.bashrc

… and add some lines to it:

# Turn on advanced bash completion if the file exists
if [ -f /usr/local/etc/bash_completion ]; then
  . /usr/local/etc/bash_completion

# Locate virtualenvwrapper binary
if [ -f ~/.local/bin/virtualenvwrapper.sh ]; then
    export VENVWRAP=~/.local/bin/virtualenvwrapper.sh

if [ ! -z $VENVWRAP ]; then
    # virtualenvwrapper -------------------------------------------
    # make sure env directory exists; else create it
    [ -d $HOME/sites/env ] || mkdir -p $HOME/sites/env
    export WORKON_HOME=$HOME/sites/env
    source $VENVWRAP

    # virtualenv --------------------------------------------------

    # pip ---------------------------------------------------------
    export PIP_DOWNLOAD_CACHE=$HOME/.pip/cache

Let’s re-load our bash environment again:

source ~/.bash_profile

I also add two customizations to virtualenv’s postactivate script, which defines what happens after activating a virtual environment:

vim ~/sites/env/postactivate

Add the following lines, taking note of the comments to see what benefits they offer:


# Add the active project to the PYTHONPATH
if [ -d ~/sites/env/$proj_name/lib/python2.7/site-packages ]; then
  add2virtualenv ~/sites/env/$proj_name/lib/python2.7/site-packages

# "cd" into the virtualenv, or its "project" folder if there is one
if [ -d ~/sites/env/$proj_name/project ]; then
  cd ~/sites/env/$proj_name/project
  cd ~/sites/env/$proj_name

Now we have virtualenv and virtualenvwrapper installed and ready to create new virtual environments, which will be stored in ~/sites/env/.

Version control

Lion comes with git, which is a welcome addition. I’m partial to Mercurial, so for those that want both options, the following command will do the trick:

easy_install --prefix=~/.local Mercurial

At a minimum, you’ll need to add a few lines to your .hgrc file in order to use Mercurial:

vim ~/.hgrc

The following lines should get you started; just be sure to change the values to your name and email address, respectively:

username = YOUR NAME <address@example.com>

To test whether Mercurial is configured and ready for use, run the following command:

hg debuginstall

If the last line in the response is “No problems detected”, then Mercurial has been installed and configured properly.

Creating virtual environments

Let’s create a virtual environment called “foobar”:

mkvirtualenv foobar

That should create a new virtual environment and automatically switch our present working directory to that new foobar environment.

In the future, when you want to work on a project contained in a virtual environment, use the “workon” command followed by the name of the virtual environment:

workon foobar

That will activate the “foobar” virtual environment, and if you’ve made the changes I recommended earlier to the “postactivate” script, you should be taken directly to the virtual environment’s directory at ~/sites/env/foobar. If not, you can always get there via the “cdvirtualenv” command, which will take you to the currently-active virtual environment’s home.

First Django project

Assuming our current working directory is our new “foobar” virtual environment, let’s create a folder within called “project” that will contain all of our to-be-versioned files:

mkdir project

While the quick way to install Django would be…

pip install Django

… a better way is to define our project’s dependencies in a requirements file:

vim project/requirements.txt

Our requirements at this point are very simple — just Django. So the contents of our requirements.txt file should just be:


We can then install Django via:

pip install -r project/requirements.txt

We’ll use Django’s “startproject” command to create a new site within our “project” folder:

django-admin.py startproject mysite project/

We can now start Django’s development web server…

cd project
python manage.py runserver

… and see the results in our web browser. For those that are new to Django, the Django Tutorial is a good place to learn how to continue building your project.

First commit

Now that you have a Django project started, use your preferred version control system to make your first commit. For Mercurial, that would be:

hg init project
cd project
hg add
hg commit -m "Initial commit"

By versioning your projects from the very beginning, you’ll always know how (and hopefully why) you’ve made your changes along the way.


While I’ve posted excerpts of my dotfiles above, you can get the whole enchilada on GitHub.


Congrats! It takes a bit of time to set up a new system, but it’s well worth the journey. These are the tools of our trade, and we use them every day, so it makes sense to make them as efficient as possible.

Want to be notified when I publish new technical tutorials?