Monday, August 3, 2009

Shortening bash prompts inside a virtualenv

This post will describe a way to shorten your bash prompt in Linux so that inside the inevitable multiple directories caused by using virtualenv, you can still have a small prompt.

I wanted to take an existing library and split it into it's own library. On my computer, I was going to work wit this library at ~/projects/sc/kespa_scrape.

Of course, that kespa_scrape directory is actually the directory holding the virtual_env...


kespa_scrape/
|-- bin
| |-- activate
| |-- activate_this.py
| |-- easy_install
| |-- easy_install-2.6
| |-- nosetests
| |-- nosetests-2.6
| |-- python
| |-- testall.sh
| `-- to3.sh
|-- include
| `-- python2.6 -> /usr/include/python2.6
|-- kespa_scrape
| `-- kespa_scrape
`-- lib
`-- python2.6


This is what the kespa_scrape directory looks like. You can see it has a kespa_scrape directory inside it as well. This directory is the root to the actual project. This directory will probably also have a kespa_scrape directory which would be the actual library package directory. Obviously, there is redundancy, but it's necessary redundancy to keep all the parts where I want them.

Some people complain about virtualenv because then you need to cd through a whole bunch of directories to start working. I make this easier with a script. This is how it is used...


gobo@gobo:~$ cd projects/sc/
gobo@gobo:~/projects/sc$ activate_project kespa_scrape
(kespa_scrape)gobo@gobo:~/projects/sc/kespa_scrape/kespa_scrape$


As you can see, I change to the directory where the project is, enter "activate_project", and it will put me into the virtual environment as well as change to the root directory of the project.

The problem I still had with this is that now my prompt is huge. I would typically change directories once more into the next kespa_scrape package, and with a large enough library name, have a prompt that easily took up half the screen.

At first, I made sure to choose short library names, but now I've come up with a better solution. It rewrites the bash prompt to replace the virtual environment directory with two tildes, and looks like this...


gobo@gobo:~$ cd projects/sc
gobo@gobo:~/projects/sc$ activate_project kespa_scrape
(kespa_scrape)gobo@gobo:~~$


Check out the difference for yourself...


(kespa_scrape)gobo@gobo:~/projects/sc/kespa_scrape/kespa_scrape$
(kespa_scrape)gobo@gobo:~~$


Here's the full script...


#!/bin/bash

# Remove possible slash at end.
DIR=${1%/}

ACTIVATE_FILE=$1/bin/activate

if [ -z "$DIR" ]; then
echo "usage: $0 directory"
return 1
fi

if [ ! -d "$DIR" ]; then
echo "Directory not found: $DIR"
return 1
fi

if [ ! -d "$DIR/$DIR" ]; then
echo "Child directory not found: $DIR/$DIR"
return 1
fi

if [ ! -e "$DIR/bin/activate" ]; then
echo "Activate file not found. Are you sure this is a virtualenv directory?"
return 1
fi


cd $DIR
source bin/activate
cd $DIR

CUR_DIR_CMD='pwd | sed -e '\''s!'$VIRTUAL_ENV/$DIR'!~~!g'\'' | sed -e '\''s!'$HOME'!~!g'\'
PS1=${PS1//\\w/$\($CUR_DIR_CMD\)}

alias cdp='cd '$VIRTUAL_ENV/$DIR


After checking to make sure all the files exist, it sets a variable called CUR_DIR_CMD. This is basically a variable that contains a bash command to take the current directory, and replace any instances of the virtual directory with ~~. Since virtualenv sets the VIRTUAL_ENV variable, it basically looks for "/projects/sc/kespa_scrape/kespa_scrape" and replaces it with ~~. It will also take the result of that and replace instances of the home directory with ~. This is so that if I change directories to something outside of the virtualenv, I can still have '~' instead of '/home/gobo'

Next, I need to replace all instances of \w in the prompt string with this expression. \w is the special string you'd use when setting your bash prompt to give you the full directory that you're in. Replacing it with my custom command allows me to replace the area where the directory should go with what I want, but leave the rest of the prompt (including virtualenv's addition to the beginning) intact.

The entire process also does pretty well when you change directories to outside the virtualenv, since you're just left with the directory...


(kespa_scrape)gobo@gobo:~~$ cd
(kespa_scrape)gobo@gobo:~$ cd projects/
(kespa_scrape)gobo@gobo:~/projects$


The only thing I want to fix up on it is to allow activate_project to work from ANY directory. Currently, it assumes that the project directory is right in front of you. This is good enough for now though.

----

Edit: Feb 18, 2010

Also, one thing to keep in mind, for those who don't really deal much with bash scripts, is that typically when you launch a script the script runs inside it's own shell, and when it completes, the shell closes. This means you lose things such as the new alias or the changes to the directory. So, you can't run the script in any of these ways:


sh /home/mark/bin/activate_project project_name
/home/mark/bin/activate_project project_name


If you did this, the script would run, but you would not see anything changed, since all the changes would be done in the shell created for the script, which exits without changing the shell that ran the script. To ensure that you run the script on the shell you run the script with, you need to use the source or dot operator.


. /home/mark/bin/activate_project project_name
source /home/mark/bin/activate_project project_name


As a side note, now you really know what that source bin/activate line does! Anyway, you don't want to deal with this hassle, so set up an alias...


alias activate_project="source /home/mark/bin/activate_project"

No comments:

Post a Comment