SaltyCrane Blog — Notes on JavaScript and web development

Install WordPress 2.8.4 on Ubuntu 9.04 Jaunty

Since we're using WordPress at work, I decided to install WordPress on my local machine for testing and educational purposes. Here are my notes. The versions of stuff are: WordPress 2.8.4, Ubuntu 9.04 Jaunty Jackalope, MySQL 5.1, PHP 5.2.6, Apache 2.2.11 with prefork mpm.

Install prerequisites

  • Install Apache and PHP
    sudo apt-get install php5
    sudo apt-get install php5-mysql
  • Install MySQL Server
    sudo apt-get install mysql-server

    Set a password for the MySQL root user when prompted.

Download Wordpress code

cd /var/www
sudo wget http://wordpress.org/wordpress-2.8.4.tar.gz
sudo tar zxvf wordpress-2.8.4.tar.gz

Create MySQL database and user

mysql -uroot -p

Enter the password you created above.

CREATE DATABASE wordpress;
CREATE USER 'wp_user'@'localhost' IDENTIFIED BY 'wp_password';
GRANT ALL PRIVILEGES ON wordpress.* TO 'wp_user'@'localhost';
\q

Edit WordPress wp-config.php file

  • Copy the sample file
    cd /var/www/wordpress
    sudo cp wp-config-sample.php wp-config.php
  • Edit the following lines in /var/www/wordpress/wp-config.php
    /** The name of the database for WordPress */
    define('DB_NAME', 'wordpress');
    
    /** MySQL database username */
    define('DB_USER', 'wp_user');
    
    /** MySQL database password */
    define('DB_PASSWORD', 'wp_password');
    
    /** MySQL hostname */
    define('DB_HOST', 'localhost');

Set up Apache virtual host

  • Edit /etc/apache2/sites-available/wordpress
    ServerName localhost
    <VirtualHost *:80>
    	DocumentRoot /var/www/wordpress
    	ErrorLog /var/log/apache2/wordpress.error.log
    </VirtualHost>
        
  • Symlink to sites-enabled
    sudo ln -s /etc/apache2/sites-available/wordpress /etc/apache2/sites-enabled/wordpress
  • Remove default virtual host from sites-enabled
    sudo rm /etc/apache2/sites-enabled/000-default

Restart Apache and view your Wordpress site

  • sudo /etc/init.d/apache2 restart
  • Go to http://localhost in your browser. You should get the WordPress Welcome page.

References

Notes on Python logging

mylogging.py:

import logging
import sys

DEBUG_LOG_FILENAME = '/var/log/my-debug.log'
WARNING_LOG_FILENAME = '/var/log/my-warning.log'

# set up formatting
formatter = logging.Formatter('[%(asctime)s] %(levelno)s (%(process)d) %(module)s: %(message)s')

# set up logging to STDOUT for all levels DEBUG and higher
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.DEBUG)
sh.setFormatter(formatter)

# set up logging to a file for all levels DEBUG and higher
fh = logging.FileHandler(DEBUG_LOG_FILENAME)
fh.setLevel(logging.DEBUG)
fh.setFormatter(formatter)

# set up logging to a file for all levels WARNING and higher
fh2 = logging.FileHandler(WARNING_LOG_FILENAME)
fh2.setLevel(logging.WARN)
fh2.setFormatter(formatter)

# create Logger object
mylogger = logging.getLogger('MyLogger')
mylogger.setLevel(logging.DEBUG)
mylogger.addHandler(sh)
mylogger.addHandler(fh)
mylogger.addHandler(fh2)

# create shortcut functions
debug = mylogger.debug
info = mylogger.info
warning = mylogger.warning
error = mylogger.error
critical = mylogger.critical

testlogging.py:

from mylogging import debug, info, warning, error

debug('debug message')
info('info message')
warning('warning message')
error('error message')

Run it:

python testlogging.py

Console output:

[2009-10-07 12:45:59,713] 10 (22886) testlogging: debug message
[2009-10-07 12:45:59,718] 20 (22886) testlogging: info message
[2009-10-07 12:45:59,718] 30 (22886) testlogging: warning message
[2009-10-07 12:45:59,719] 40 (22886) testlogging: error message

cat debug.log:

[2009-10-07 12:45:59,713] 10 (22886) testlogging: debug message
[2009-10-07 12:45:59,718] 20 (22886) testlogging: info message
[2009-10-07 12:45:59,718] 30 (22886) testlogging: warning message
[2009-10-07 12:45:59,719] 40 (22886) testlogging: error message

cat warning.log:

[2009-10-07 12:45:59,718] 30 (22886) testlogging: warning message
[2009-10-07 12:45:59,719] 40 (22886) testlogging: error message

Note: if you get a permission denied error for the log file, you can do this:

sudo touch /var/log/my-debug.log
sudo touch /var/log/my-warning.log
sudo chmod 666 /var/log/my-debug.log
sudo chmod 666 /var/log/my-warning.log

Documentation

Notes on Python Fabric 0.9b1

Fabric is a Python package used for deploying websites or generally running commands on a remote server. I first used Fabric about a year ago and thought it was great. Since then, Fabric has procured a new maintainer, a new domain, and a few new revisions.

Here are my notes on installing the latest stable version (0.9b1) on Ubuntu Jaunty and running a simple example.

Install Fabric 0.9b1

  • Install Easy Install & pip
    sudo apt-get install python-setuptools python-dev build-essential
    sudo easy_install -U pip
  • Install Fabric

    Note: According to the Fabric website, the latest version of the prerequisite Python library, Paramiko has a bug, so it is recommended to install the previous version, 1.7.4, instead. This can be accomplished by creating a requirements file for pip:

    http://www.lag.net/paramiko/download/paramiko-1.7.4.tar.gz
    http://git.fabfile.org/cgit.cgi/fabric/snapshot/fabric-0.9b1.tar.gz

    To install, use the pip install command with the -r option and the path to your requirements file. For convenience, you can install Fabric using my requirements file:

    sudo pip install -r http://www.saltycrane.com/site_media/code/fabric-requirements.txt

Using Fabric

  • Create a file called fabfile.py in ~/myproject:
    from __future__ import with_statement # needed for python 2.5
    from fabric.api import env, run
    
    def ec2():
        env.hosts = ['ec2-65-234-55-183.compute-1.amazonaws.com']
        env.user = 'saltycrane'
        env.key_filename = '/path/to/my/id_ssh_keyfile'
    
    def ps_apache():
        run('ps -e -O rss,pcpu | grep apache')
    
  • Run it
    cd ~/myproject
    fab ec2 ps_apache

    Results:

    [ec2-65-234-55-183.compute-1.amazonaws.com] run: ps -e -O rss,pcpu | grep apache
    [ec2-65-234-55-183.compute-1.amazonaws.com] err: stdin: is not a tty
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  3571 10996  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5047 28352  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5048 27756  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5049 23752  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5050 27344  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5055 27344  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5166 28404  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  5167 27900  0.0 S ?        00:00:00 /usr/sbin/apache2 -k start
    [ec2-65-234-55-183.compute-1.amazonaws.com] out:  9365  1208  0.0 S ?        00:00:00 /bin/bash -l -c ps -e -O rss,pcpu | grep apache
    
    Done.
    Disconnecting from ec2-65-234-55-183.compute-1.amazonaws.com... done.

List of available env options

I extracted this list from state.py (0.9b1). Or view the tip version

env.reject_unknown_hosts = True         # reject unknown hosts
env.disable_known_hosts = True          # do not load user known_hosts file
env.user = 'username'                   # username to use when connecting to remote hosts
env.password = 'mypassword'             # password for use with authentication and/or sudo
env.hosts = ['host1.com', 'host2.com']  # comma-separated list of hosts to operate on
env.roles = ['web']                     # comma-separated list of roles to operate on
env.key_filename = 'id_rsa'             # path to SSH private key file. May be repeated.
env.fabfile = '../myfabfile.py'         # name of fabfile to load, e.g. 'fabfile.py' or '../other.py'
env.warn_only = True                    # warn, instead of abort, when commands fail
env.shell = '/bin/sh'                   # specify a new shell, defaults to '/bin/bash -l -c'
env.rcfile = 'myfabconfig'              # specify location of config file to use
env.hide = ['everything']               # comma-separated list of output levels to hide
env.show = ['debug']                    # comma-separated list of output levels to show
env.version = '1.0'
env.sudo_prompt = 'sudo password:'
env.use_shell = False
env.roledefs = {'web': ['www1', 'www2', 'www3'],
                'dns': ['ns1', 'ns2'],
                }
env.cwd = 'mydir'

How to check the status code of a command

To check the return code of your command, set the env.warn_only option to True and check the return_code attribute of object returned from run(). For example:

def ec2():
    env.hosts = ['ec2-65-234-55-183.compute-1.amazonaws.com']
    env.user = 'saltycrane'
    env.key_filename = '/path/to/my/id_ssh_keyfile'
    env.warn_only = True

def getstatus():
    output = run('ls non_existent_file')
    print 'output:', output
    print 'failed:', output.failed
    print 'return_code:', output.return_code
fab ec2 getstatus
[ec2-65-234-55-183.compute-1.amazonaws.com] run: ls non_existent_file
[ec2-65-234-55-183.compute-1.amazonaws.com] err: ls: cannot access non_existent_file: No such file or directory

Warning: run() encountered an error (return code 2) while executing 'ls non_existent_file'

output:
failed: True
return_code: 2

Done.
Disconnecting from ec2-65-234-55-183.compute-1.amazonaws.com... done.

Links

Other notes

  • Error message: paramiko.SSHException: Channel closed.

    Try using Paramiko version 1.7.4 instead of 1.7.5. See http://www.mail-archive.com/[email protected]/msg00844.html.

  • How to check the version of Paramiko:
    $ python
    Python 2.6.2 (release26-maint, Apr 19 2009, 01:56:41) 
    [GCC 4.3.3] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import paramiko
    >>> paramiko.__version__
    '1.7.5 (Ernest)'
  • Error message: Fatal error: No existing session

    This occurred when I used the wrong username.

Notes on working with files and directories in Python

Documentation:

How to list files in a directory

See my separate post: How to list the contents of a directory with Python

How to rename a file: os.rename

Documentation: http://docs.python.org/library/os.html#os.rename

import os
os.rename("/tmp/oldname", "/tmp/newname")

How to imitate mkdir -p

import os
if not os.path.exists(directory):
    os.makedirs(directory)

How to imitate cp -r (except copy only files including hidden dotfiles)

What didn't work for my purpose:

import os

def _copy_dash_r_filesonly(src, dst):
    """Like "cp -r src/* dst" but copy files only (don't include directories)
    (and include hidden dotfiles also)
    """
    for (path, dirs, files) in os.walk(src):
        for filename in files:
            srcfilepath = os.path.join(path, filename)
            dstfilepath = os.path.join(dst, os.path.relpath(srcfilepath, src))
            dstdir = os.path.dirname(dstfilepath)
            if not os.path.exists(dstdir):
                run('mkdir -p %s' % dstdir)
            run('cp -f %s %s' % (srcfilepath, dstfilepath))

Python optparse example

Optparse seems like a pretty cool module for processing command line options and arguments in Python. It is intended to be an improvement over the old getopt module. Optparse supports short style options like -x, long style options like --xhtml and positional arguments. Optparse also makes it easy to add default options and help text. Below is my optparse example. For more information, see the optparse documentation.

Here is my example file, optparse_ex.py:

#!/usr/bin/env python

from optparse import OptionParser

def main():
    parser = OptionParser(usage="usage: %prog [options] filename",
                          version="%prog 1.0")
    parser.add_option("-x", "--xhtml",
                      action="store_true",
                      dest="xhtml_flag",
                      default=False,
                      help="create a XHTML template instead of HTML")
    parser.add_option("-c", "--cssfile",
                      action="store", # optional because action defaults to "store"
                      dest="cssfile",
                      default="style.css",
                      help="CSS file to link",)
    (options, args) = parser.parse_args()

    if len(args) != 1:
        parser.error("wrong number of arguments")

    print options
    print args

if __name__ == '__main__':
    main()

Note: If you do not specify a dest variable, optparse will use the long option name you specify (removing the initial two dashes and substituting underscores for dashes in the middle of the name). If you do not specify a long option, it will use the short option.

displaying the help message

./optparse_ex.py -h
Usage: optparse_ex.py [options] filename

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -x, --xhtml           create a XHTML template instead of HTML
  -c CSSFILE, --cssfile=CSSFILE
                        CSS file to link

wrong number of arguments

./optparse_ex.py
Usage: optparse_ex.py [options] filename

optparse_ex.py: error: wrong number of arguments

using default options

./optparse_ex.py myfile.html
{'xhtml_flag': False, 'cssfile': 'style.css'}
['myfile.html']

specifying command line options

./optparse_ex.py -x -c mystyle.css myfile.html
{'xhtml_flag': True, 'cssfile': 'mystyle.css'}
['myfile.html']

specifying long style command line options

./optparse_ex.py --xhtml --cssfile=mystyle.css myfile.html
{'xhtml_flag': True, 'cssfile': 'mystyle.css'}
['myfile.html']


How to specify a discrete list of choices for an option

from optparse import OptionParser

def main():
    parser = OptionParser(usage='usage: %prog [options] ')
    parser.add_option('-e', '--env',
                      type='choice',
                      action='store',
                      dest='environment',
                      choices=['production', 'staging', 'testing',],
                      default='production',
                      help='Environment to run on',)
    (options, args) = parser.parse_args()

Notes on migrating this blog from SQLite to PostgreSQL using Django

Here are my notes on migrating this blog from SQLite to PostgreSQL. For the parts of my database that were in proper order, the migration was made very easy using Django's ./manage.py dumpdata and ./manage.py loaddata commands. However, the database tables used for storing the comments on this blog were kind of screwed up because I had previously migrated them from my old Blogger blog. So I had to write another (not so pretty) script for that.

Thanks to this article for showing me how to use Django's dumpdata and loaddata for this migration.

Create a new Postgres database

See my previous notes for creating a Postgres database. I named the new database "saltycrane_db" owned by "django_user" with password "my_password".

Clone and modify Django project

  • Clone my SaltyCrane project
    hg clone SaltyCrane SaltyCraneNew
    or
    cp -rp SaltyCrane SaltyCraneNew
  • Edit settings.py:
    DATABASE_ENGINE = 'postgresql_psycopg2'
    DATABASE_NAME = 'saltycrane_db'
    DATABASE_USER = 'django_user'
    DATABASE_PASSWORD = my_password'
  • Create database tables
    python manage.py syncdb

Migrate data

  • Create JSON dumps from the existing SQLite database for my 3 Django apps (myblogapp, comments, and tagging):
    cd /srv/SaltyCrane/iwiwdsmi
    ./manage.py dumpdata myblogapp > dump_myblogapp.json
    ./manage.py dumpdata comments > dump_comments.json
    ./manage.py dumpdata tagging > dump_tagging.json
  • Load the JSON data dumps to the new Postgres database:
    cd /srv/SaltyCraneNew/iwiwdsmi
    ./manage.py loaddata ../../SaltyCrane/iwiwdsmi/dump_tagging.json
    ./manage.py loaddata ../../SaltyCrane/iwiwdsmi/dump_myblogapp.json

However, loading comments didn't work because I had some missing fields so...

Migration script

...I wrote a migration script:

import setup_environment

import simplejson
from pprint import pprint
from django.contrib.comments.models import Comment
from django.contrib.contenttypes.models import ContentType
from iwiwdsmi.myblogapp.models import Post

JSON_FILENAME = "/srv/SaltyCrane/iwiwdsmi/dump_comments.json"
ct = ContentType.objects.get(name='post', app_label='myblogapp')
N_OLD_COMMENTS = 4000

def main():
    delete_all_comments()
    create_dummy_comments()
    pydata = open_json_file(JSON_FILENAME)
    save_items_to_database(pydata)
    delete_dummy_comments()

def delete_all_comments():
    """ Clears the database of all comments.
    """
    comments = Comment.objects.all()
    comments.delete()
    print "All comments deleted."

def create_dummy_comments():
    """ Create a bunch of filler dummy comments
    """
    for i in range(N_OLD_COMMENTS):
        c = Comment()
        c.comment = "Filler comment."
        c.content_type = ct
        c.ip_address = None
        c.is_public = False
        c.is_removed = False
        c.object_pk = 243
        c.site_id = 1
        c.user_email = "[email protected]"
        c.user_name = "Filler"
        c.save()
    print "Filler comments created."

def delete_dummy_comments():
    comments = Comment.objects.filter(is_public=False)
    comments.delete()
    print "Dummy comments deleted."

def open_json_file(filename):
    """ open the json file and return the python data structure
    """
    json_fp = open(filename)
    pydata = simplejson.load(json_fp)
    return pydata

def save_items_to_database(pydata):
    """ Process the python data structure and save to the new database
    """
    for item in pydata:
        pprint(item)
        pk = item['pk']
        item = item['fields']
        if item['is_public'] and not item['is_removed']:
            c = Comment.objects.get(id=pk)
            c.comment = item['comment']
            c.content_type = ct
            c.ip_address = get_ip_address(item['ip_address'])
            c.is_public = item['is_public']
            c.is_removed = item['is_removed']
            c.object_pk = item['object_pk']
            c.site_id = 1
            c.submit_date = item['submit_date']
            c.user_id = item['user']
            c.user_email = item['user_email']
            c.user_name = item['user_name']
            c.user_url = item['user_url']
            c.save()

def get_ip_address(ip):
    """ Handle bad input for IP addresses
    """
    if ip == "" or ip == "unknown":
        return None
    else:
        return ip

if __name__ == '__main__':
    main()

Example of hg convert on Ubuntu

Converting a Subversion repository to a Mercurial repository is very easy. Here's an example that uses hg convert to convert a SVN repository (django-tagging) to Mecurial on Ubuntu. I'm using Mercurial 1.3 on Ubuntu 9.04.

  • Install Subversion Python bindings. (This should solve the "Subversion python bindings could not be loaded" error)
    sudo apt-get install python-subversion
  • Enable the "convert" extension. Add the following to your ~/.hgrc file:
    [extensions]
    hgext.convert =
  • Convert:
    hg convert http://django-tagging.googlecode.com/svn/trunk/ django-tagging-hg
  • Update:
    cd django-tagging-hg
    hg update

Using psycopg2 with virtualenv on Ubuntu JauntyMaverick

Update 2009-11-02: Well I am dumb-- psycopg2 can be installed with pip/easy_install. The reason I got the error before was because I didn't have the required dependencies installed. On Ubuntu, I used apt-get build-dep. So, here's the summary:

Update 2009-11-11: My update doesn't work. See comments #4 and #5 below.

Update 2010-05-17: Here is what finally worked for me using Python 2.6 on Ubuntu 10.04 Lucid Lynx using virtualenv 1.4.8 and pip 0.7.1. Thanks to Daniel for the final piece of the solution.

Update 2011-05-13: Looks like the mx+virtualenv issue has been reported and fixed to be released in 2.4.2. via @psycopg's tweet

Update 2011-11-05: psycopg2 2.4.2 has been released with the mxDateTime fix so the install is very easy now.

Install dependencies

$ sudo apt-get build-dep python-psycopg2

Install pyscopg2 in a virtualenv

$ virtualenv --no-site-packages myenv 
$ source myenv/bin/activate 
$ pip install psycopg2 

No longer needed: $ easy_install -i http://downloads.egenix.com/python/index/ucs4/ egenix-mx-base



*** THE REST OF THIS POST DESCRIBES MY INITIAL OUTDATED SOLUTION. ***

I recently starting using virtualenv for creating isolated Python environments. Virtualenv has made it easy to manage different Python configurations for different websites, but I was slowed down a bit trying to use psycopg2, the Python-PostgreSQL database adapter, with virtualenv after upgrading to Ubuntu 9.04 Jaunty Jackalope.

Currently, virtualenv (1.3.3) doesn't find Ubuntu Jaunty's Python packages. In Ubuntu Jaunty, the default Python version changed from 2.5 to 2.6. More importantly, the site-packages directory was changed to dist-packages. Prior to Ubuntu 9.04, Ubuntu's Python packages, such as python-psycopg2, were installed to /usr/lib/python2.5/site-packages. Now Ubuntu's Python packages are installed to /usr/lib/python2.6/dist-packages. (See this discussion at the virtualenv group for more information.)

As a result of this change, virtualenv (as of 1.3.3) doesn't find Ubuntu's Python packages installed using apt-get. My solution was to create symlinks to the desired packages and egg-info files in site-packages. I'm not sure if this is the proper way to handle this. If there is a better solution, please let me know. One advantage of using this method is that I don't need to clutter my virtualenv with all the packages that have accumulated in my global site-packagesdist-packages.

Install easy_install, pip, and virtualenv

sudo apt-get install python-setuptools python-dev build-essential
sudo easy_install -U pip
sudo pip install -U virtualenv

Install Ubuntu's psycopg2 package

sudo apt-get install python-psycopg2

Symlink the psycopg2 (and mxDateTime) files

sudo mkdir /usr/lib/python2.6/site-packages
sudo ln -s /usr/lib/python2.6/dist-packages/psycopg2 /usr/lib/python2.6/site-packages
sudo ln -s /usr/lib/python2.6/dist-packages/psycopg2-2.0.8.egg-info /usr/lib/python2.6/site-packages
sudo ln -s /usr/lib/python2.6/dist-packages/mx /usr/lib/python2.6/site-packages

Create a virtualenv

virtualenv myenv

Check what's available

pip freeze -E myenv

Results:

psycopg2==2.0.8
wsgiref==0.1.2


Note: you might wonder why I didn't do a pip install -E myenv psycopg2. I tried this, but got an error. Maybe psycopg2 doesn't support pip/easy_install? Here is my error message:

Downloading/unpacking psycopg2
  Downloading psycopg2-2.0.11.tar.gz (255Kb): 255Kb downloaded
  Running setup.py egg_info for package psycopg2
    error: No such file or directory
    Complete output from command python setup.py egg_info:
    running egg_info

creating pip-egg-info/psycopg2.egg-info

writing pip-egg-info/psycopg2.egg-info/PKG-INFO

writing top-level names to pip-egg-info/psycopg2.egg-info/top_level.txt

writing dependency_links to pip-egg-info/psycopg2.egg-info/dependency_links.txt

writing manifest file 'pip-egg-info/psycopg2.egg-info/SOURCES.txt'

warning: manifest_maker: standard file '-c' not found

error: No such file or directory

----------------------------------------
Command python setup.py egg_info failed with error code 1
Storing complete log in ./pip-log.txt
Complete output from command temp/bin/python /usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py install -E temp psycopg2 temp ___VENV_RESTART___:

----------------------------------------
Traceback (most recent call last):
  File "/usr/local/bin/pip", line 3, in 
    pip.main()
  File "/usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py", line 926, in main
    return command.main(initial_args, args[1:], options)
  File "/usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py", line 258, in main
    restart_in_venv(options.venv, site_packages, complete_args)
  File "/usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py", line 1009, in restart_in_venv
    call_subprocess([python, file] + args + [base, '___VENV_RESTART___'])
  File "/usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py", line 3643, in call_subprocess
    % (command_desc, proc.returncode))
pip.InstallationError: Command temp/bin/python /usr/local/lib/python2.6/dist-packages/pip-0.4-py2.6.egg/pip.py install -E temp psycopg2 temp ___VENV_RESTART___ failed with error code 1

Install wmii snapshot 20090703 on Ubuntu Jaunty

On Friday I installed the latest snapshot of wmii, the lightweight, tiling, dynamic, scriptable, keyboard navigatable window manager. (See my wmii tag for more posts about wmii.) Wmii orangizes windows into columns. Traditionally there has been three modes for each column: default, stack, and max. "Default" shows all windows in a column. "Stack" shows only one window at a time with the title bars of the other windows showing. "Max" shows only one window without any other titlebars. I usually use two columns each in stack mode. To navigate windows, I use Mod4+j/k to move among windows in a column and Mod4+h/l to move between columns. This has worked well for me, but sometimes I wanted to display two windows in a column while keeping the rest of the windows hidden. Thankfully, the latest snapshot of wmii now includes a new mode which solves my problem. It is a hybrid of the "default" and "stack" modes. It is called "defalut-max" and it allows for multiple stacks of windows per column. There is also a "default+max" mode which is like a hybrid of the old "default" and "max" modes. Additionally these modes now apply to the floating layer as well.

Another really cool thing about the latest wmii snapshot is that it now includes a Python library to interface with the wmii filesystem and a wmiirc written in Python. This should make it a lot easier to script wmii. This is exciting stuff. Thanks to Kris Maglione and the wmii contributors.

Here are my notes for installing wmii snapshot 20090703 on Ubuntu 9.04 Jaunty.

Install prerequisites

  • $ sudo apt-get install build-essential xorg-dev
    $ sudo apt-get install dwm-tools
  • This is so it can find the freetype headers.
    $ sudo ln -s /usr/include/freetype2/freetype /usr/include/freetype

Install wmii

  • Download wmii+ixp-snap20090703.tgz from the wmii homepage and save it to ~/incoming.
  • Unpack
    $ cd ~/incoming
    $ tar -zxvf wmii+ixp-snap20090703.tgz
  • make config
    $ cd ~/incoming/wmii+ixp-snap20090703
    $ make config
    Accept all defaults except set PREFIX=/home/saltycrane/lib/wmii_20090703

  • make & make install
    $ make 
    $ make install
  • Create links in my ~/bin directory:
    $ cd ~/bin
    $ ln -s ~/lib/wmii_20090703/bin/* .

Use the fun Python wmiirc

  • Copy the Python files to ~/.wmii-hg. This includes the pygmi directory, the pyxp directory, and the two wmiirc files.
    $ cp -r ~/incoming/wmii+ixp-snap20090703/alternative_wmiircs/python/* ~/.wmii-hg/
  • Edit ~/.wmii-hg/wmiirc.py as desired.

Run wmii

  • Create ~/.xsession:
    xmodmap ~/.Xmodmap
    gnome-screensaver&
    gnome-power-manager&
    nm-applet --sm-disable&
    urxvt&
    
    until wmii; do
        true
    done
  • Log out of the current window manager and then select X client script as the session and log in.

Install slock screen locker

  • Download from http://tools.suckless.org/slock
  • $ tar zxvf slock-0.9.tar.gz 
    $ cd slock-0.9 
    $ sudo make clean install 
    
  • Running slock: Simply invoke the 'slock' command. To get out of it, enter your password.

Freetype header error

The first time around, I got the following error:

In file included from /usr/include/X11/Xft/Xft.h:39,
                 from ../include/x11.h:9,
                 from cmd/wmii9menu.c:49:
/usr/include/ft2build.h:56:38: error: freetype/config/ftheader.h: No such file or directory
In file included from ../include/x11.h:9,
                 from cmd/wmii9menu.c:49:
/usr/include/X11/Xft/Xft.h:40:10: error: #include expects "FILENAME" or 
In file included from ../include/x11.h:9,
                 from cmd/wmii9menu.c:49:
/usr/include/X11/Xft/Xft.h:60: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘_XftFTlibrary’
/usr/include/X11/Xft/Xft.h:94: error: expected specifier-qualifier-list before ‘FT_UInt’
/usr/include/X11/Xft/Xft.h:101: error: expected specifier-qualifier-list before ‘FT_UInt’
/usr/include/X11/Xft/Xft.h:198: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/include/X11/Xft/Xft.h:303: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/include/X11/Xft/Xft.h:362: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘XftLockFace’
/usr/include/X11/Xft/Xft.h:401: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/include/X11/Xft/Xft.h:407: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
/usr/include/X11/Xft/Xft.h:416: error: expected declaration specifiers or ‘...’ before ‘FT_UInt’
/usr/include/X11/Xft/Xft.h:417: error: expected declaration specifiers or ‘...’ before ‘FT_UInt’
/usr/include/X11/Xft/Xft.h:426: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘XftCharIndex’
/usr/include/X11/Xft/Xft.h:459: error: expected ‘;’, ‘,’ or ‘)’ before ‘*’ token
make[1]: *** [wmii9menu.o] Error 1
make: *** [dall] Error 2

Per this discussion, I solved the problem by creating a symbolic link:

$ sudo ln -s /usr/include/freetype2/freetype /usr/include/freetype

Notes on C++ development with Emacs on Ubuntu Linux

Here are my notes on setting up an Emacs C++ development environment. I'm using GNU g++ (4.3.3), GNU make (3.81), GNU gdb (3.8-debian), and GNU emacs (23.0.92.1 built from CVS 2009-04-22) on Ubuntu Linux (9.04 Jaunty Jackalope).

Install tools

If you don't already have g++, make, and gdb, install them:

sudo apt-get install g++ make gdb

Create files

  • Create a project directory:
    mkdir helloworld
  • Create a source file helloworld/hello.cpp:
    #include <iostream>
    
    int main(void) 
    {
       char greeting[] = "Hello World!";
       std::cout << greeting << std::endl;
       return 0;
    }
  • Create a makefile helloworld/Makefile:
    # Makefile for GNU g++
    
    CC=g++
    CFLAGS=-g -Wall
    all: hello
    
    hello: hello.cpp
    	$(CC) $(CFLAGS) -o hello hello.cpp
    
    clean:
    	rm hello
    

Compile within Emacs

See also: 32 Compiling and Testing Programs in the GNU Emacs manual.

  • Open hello.cpp in Emacs
  • M-x compile RET
    make -k RET

    Note: The default compile command is make -k. You can change the default command by setting the compile-command variable in your .emacs file.

  • Useful commands in the compilation window:
    • C-x `: Go to next error in the code
    • M-n: Go to next error message
    • M-p: Go to previous error message
    • RET: Go to the source code for the current error message

Run using Emacs shell

  • M-! ./hello RET

    You should see the output in the minibuffer.

Debug with gdb in Emacs

For more information see: 32.6 Running Debuggers Under Emacs in the GNU Emacs manual.

  • To show multiple debugging windows such as breakpoints, locals, etc., set the gdb-many-windows variable in ~/.emacs:
    (setq gdb-many-windows t)
    Restart Emacs.

  • Start the debugger. While visiting helloworld/hello.cpp:
    M-x gdb RET --annotate=3 hello RET
  • Set a breakpoint by clicking in the left margin at the desired location.
  • Run the debugger by typing run at the (gdb) prompt in the *gud-hello* buffer.
  • In the GUD buffer, use the following commands:
    • C-c C-s Step into
    • C-c C-n Stev over
    • C-c C-p Evaluate the expression at point.
    • C-c C-r Continue
    • C-c C-u Continue until current line

  • When finished, type quit at the (gdb) prompt.

Documentation:

References

  • Practical C++ Programming, 2nd Edition by Steve Oualine (2004)
  • Learning GNU Emacs, 3rd Edition by Debra Cameron et al. (2004)
  • Emacs: the Free Software IDE at the Linux Journal (2002)