Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Django Blog Project #9: Migrating Blogger posts with Beautiful Soup



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/07/django-blog-project-9-migrating-blogger/

You should be redirected in 2 seconds.



Last post, I talked about adding comments to my new sample blog application. This was about the last basic feature I needed to add before I started actually using it for real. Of course there are still a number of features I'd like to add, such as automatic syntax highlighting with Pygments, and incorporating django-tagging and some more intersting views, not to mention comment moderation. But I think those will have to wait-- I want to start using my new blog for real sometime.

So for the past few days, I've been working on my Beautiful Soup screen scraper script to copy all my Blogger posts over to my new Django blog. Initial results came quickly (it's pretty cool to see such a huge data dump after only a few lines of Beautiful Soup'ing) but the details (especially with the comments) kind of slowed me down. I've finally got everything copied over to my satisfaction. Below is the script I used to do it. Note, I realize it's not pretty-- just a one time use hack. But hopefully someone else doing the same thing might find it useful.

#!/usr/bin/env python

import datetime
import os
import re
import urllib2
from BeautifulSoup import BeautifulSoup
from myblogapp.models import Post, LegacyComment
from django.contrib.comments.models import FreeComment

URL = ''.join([
        'http://iwiwdsmi.blogspot.com/search?',
        'updated-min=2006-01-01T00%3A00%3A00-08%3A00&'
        'updated-max=2009-01-01T00%3A00%3A00-08%3A00&',
        'max-results=1000'
        ])
html = urllib2.urlopen(URL).read()
soup = BeautifulSoup(html)

for post in soup.html.body.findAll('div', {'class': 'post'}):
    print
    print '--------------------------------------------------------------'

    # save the post title and permalink
    h3 = post.find('h3', {'class': 'post-title'})
    post_href = h3.find('a')['href']
    post_title = h3.find('a').string
    post_slug = os.path.basename(post_href).rstrip('.html')
    print post_slug
    print post_href
    print post_title

    # save the post body
    div = post.find('div', {'class': 'post-body'})
    [toremove.extract() for toremove in div.findAll('script')]
    [toremove.extract() for toremove in div.findAll('span', {'id': 'showlink'})]
    [toremove.extract() for toremove in div.findAll('div', {'style': 'clear: both;'})]
    [toremove.parent.extract() for toremove in div.findAll(text='#fullpost{display:none;}')]
    post_body = ''.join([str(item)
                         for item in div.contents
                         ]).rstrip()
    post_body = re.sub(r"iwiwdsmi\.blogspot\.com/(\d{4}/\d{2}/[\w\-]+)\.html", 
                       r"www.saltycrane.com/blog/\1/", 
                       post_body)

    # count number of highlighted code sections 
    highlight = div.findAll('div', {'class': 'highlight'})
    if highlight:
        hl_count += len(highlight)
        hl_list.append(post_title)

    # save the timestamp
    a = post.find('a', {'class': 'timestamp-link'})
    try:
        post_timestamp = a.string
    except:
        match = re.search(r"\.com/(\d{4})/(\d{2})/", post_href)
        if match:
            year = match.group(1)
            month = match.group(2)
        post_timestamp = "%s/01/%s 11:11:11 AM" % (month, year)
    print post_timestamp

    # save the tags (this is ugly, i know)
    if 'error' in post_title.lower():
        post_tags = ['error']
    else:
        post_tags = []
    span = post.find('span', {'class': 'post-labels'})
    if span:
        a = span.findAll('a', {'rel': 'tag'})
    else:
        a = post.findAll('a', {'rel': 'tag'})
    post_tags = ' '.join([tag.string for tag in a] + post_tags)
    if not post_tags:
        post_tags = 'untagged'
    print post_tags

    # add Post object to new blog
    if True:
        p = Post()
        p.title = post_title
        p.body = post_body
        p.date_created = datetime.datetime.strptime(post_timestamp, "%m/%d/%Y %I:%M:%S %p")
        p.date_modified = p.date_created
        p.tags = post_tags
        p.slug = post_slug
        p.save()

    # check if there are comments
    a = post.find('a', {'class': 'comment-link'})
    if a:
        comm_string = a.string.strip()
    else:
        comm_string = "0"
    if comm_string[0] != "0":
        print
        print "COMMENTS:"

        # get the page with comments
        html_single = urllib2.urlopen(post_href).read()
        soup_single = BeautifulSoup(html_single)

        # get comments
        comments = soup_single.html.body.find('div', {'class': 'comments'})
        cauth_list = comments.findAll('dt')
        cbody_list = comments.findAll('dd', {'class': 'comment-body'})
        cdate_list = comments.findAll('span', {'class': 'comment-timestamp'})

        if not len(cauth_list)==len(cbody_list)==len(cdate_list):
            raise "didn't get all comment data"

        for auth, body, date in zip(cauth_list, cbody_list, cdate_list):
            
            # create comment in database
            lc = LegacyComment()
            lc.body = str(body.p)

            # find author
            lc.author = "Anonymous"
            auth_a = auth.findAll('a')[-1]
            auth_no_a = auth.contents[2]
            if auth_a.string:
                lc.author = auth_a.string
            elif auth_no_a:
                match = re.search(r"\s*([\w\s]*\w)\s+said", str(auth_no_a))
                if match:
                    lc.author = match.group(1)
            print lc.author

            # find website
            try:
                lc.website = auth_a['href']
            except KeyError:
                lc.website = ''
            print lc.website

            # other info
            lc.date_created = datetime.datetime.strptime(
                date.a.string.strip(), "%m/%d/%Y %I:%M %p")
            print lc.date_created
            lc.date_modified = lc.date_created
            lc.post_id = p.id
            lc.save()

I also made some changes to my Django blog code as I migrated my Blogger posts. The main addition was a LegacyComment model along with the associated views and templates. My Blogger comments consisted of HTML markup, but I didn't want to allow arbitrary HTML in my new comments for fear of cross site scripting. So I separated my legacy Blogger comments from my new Django site comments.



models.py

Here are my model changes. I added a LegacyComment class which contains pertinent comment attributes and a ForeignKey to the post that it belongs to. I also added a lc_count (for legacy comment count) field to the Post class which stores the number of comments for the post. It is updated by the save() method in the LegacyComment class every time a comment is saved. Hmmm, I just realized the count will be wrong if I ever edit these comments. Well, since these are legacy comments, hopefully I won't have to edit them.

~/src/django/myblogsite/myblogapp/models.py:
import re
from django.db import models

class Post(models.Model):
    title = models.CharField(maxlength=200)
    slug = models.SlugField(maxlength=100)
    date_created = models.DateTimeField() #auto_now_add=True)
    date_modified = models.DateTimeField()
    tags = models.CharField(maxlength=200)
    body = models.TextField()
    body_html = models.TextField(editable=False, blank=True)
    lc_count = models.IntegerField(default=0, editable=False)

    def get_tag_list(self):
        return re.split(" ", self.tags)

    def get_absolute_url(self):
        return "/blog/%d/%02d/%s/" % (self.date_created.year,
                                      self.date_created.month,
                                      self.slug)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ["-date_created"]

    class Admin:
        pass

class LegacyComment(models.Model):
    author = models.CharField(maxlength=60)
    website = models.URLField(core=False)
    date_created = models.DateTimeField()
    date_modified = models.DateTimeField()
    body = models.TextField()
    post = models.ForeignKey(Post)

    def save(self):
        p = Post.objects.get(id=self.post.id)
        p.lc_count += 1
        p.save()
        super(LegacyComment, self).save()

    class Meta:
        ordering = ["date_created"]

    class Admin:
        pass


views.py

Here is an excerpt from my views.py file showing the changes:

~/src/django/myblogsite/myblogapp/views.py:
import re
from datetime import datetime
from django.shortcuts import render_to_response
from myblogsite.myblogapp.models import Post, LegacyComment

MONTH_NAMES = ('', 'January', 'Feburary', 'March', 'April', 'May', 'June', 'July',
               'August', 'September', 'October', 'November', 'December')
MAIN_TITLE = "Sofeng's Blog 0.0.7"

def frontpage(request):
    posts, pagedata = init()
    posts = posts[:5]
    pagedata.update({'post_list': posts,
                     'subtitle': '',})
    return render_to_response('listpage.html', pagedata)

def singlepost(request, year, month, slug2):
    posts, pagedata = init()
    post = posts.get(date_created__year=year,
                            date_created__month=int(month),
                            slug=slug2,)
    legacy_comments = LegacyComment.objects.filter(post=post.id)
    pagedata.update({'post': post,
                     'lc_list': legacy_comments,})
    return render_to_response('singlepost.html', pagedata)


Templates

In the list page template I used the truncatewords_html template filter to show a 50 word post summary on the list pages instead of the full post. I also added the legacy comment count with the Django free comment count to display the total number of comments.

Excerpt from ~/src/django/myblogsite/templates/listpage.html:
{% block main %}
  <br>
  {% for post in post_list %}
    <h4><a href="/blog/{{ post.date_created|date:"Y/m" }}/{{ post.slug }}/">
        {{ post.title }}</a>
    </h4>
    {{ post.body|truncatewords_html:"50" }}
    <a href="{{ post.get_absolute_url }}">Read more...</a><br>
    <br>
    <hr>
    <div class="post_footer">
      {% ifnotequal post.date_modified.date post.date_created.date %}
        Last modified: {{ post.date_modified.date }}<br>
      {% endifnotequal %}
      Date created: {{ post.date_created.date }}<br>
      Tags: 
      {% for tag in post.get_tag_list %}
        <a href="/blog/tag/{{ tag }}/">{{ tag }}</a>{% if not forloop.last %}, {% endif %}
      {% endfor %}
      <br>

      {% get_free_comment_count for myblogapp.post post.id as comment_count %}
      <a href="{{ post.get_absolute_url }}#comments">
        {{ comment_count|add:post.lc_count }} 
        Comment{{ comment_count|add:post.lc_count|pluralize}}</a>

    </div>
    <br>
  {% endfor %}
{% endblock %}

In the single post template, I added the display of the Legacy comments in addition to the Django free comments.

Excerpt from ~/src/django/myblogsite/templates/singlepost.html:
  <a name="comments"></a>
  {% if lc_list %}
    <h4>{{ lc_list|length }} Legacy Comment{{lc_list|length|pluralize}}</h4>
  {% endif %}
  {% for legacy_comment in lc_list %}
    <br>
    <a name="lc{{ legacy_comment.id }}" href="#lc{{ legacy_comment.id }}">
      #{{ forloop.counter }}</a>
    {% if legacy_comment.website %}
      <a href="{{ legacy_comment.website }}">
        <b>{{ legacy_comment.author|escape }}</b></a> 
    {% else %}
      <b>{{ legacy_comment.author|escape }}</b>
    {% endif %}
    commented,
    on {{ legacy_comment.date_created|date:"F j, Y" }} 
    at {{ legacy_comment.date_created|date:"P" }}:
    {{ legacy_comment.body }}
  {% endfor %}
  <br>

That's it. Hopefully, I can start using my new blog soon. Please browse around on the new Django site and let me know if you run across any problems. When everything looks to be OK, I'll start posting only on my new Django site.

Here is a snapshot screenshot of version 0.0.8:


The live site can be viewed at: http://saltycrane.com/blog


Related posts:
   Django Blog Project #1: Creating a basic blog
   Django Blog Project #2: Deploying at Webfaction
   Django Blog Project #3: Using CSS and Template Inheritance
   Django Blog Project #4: Adding post metadata
   Django Blog Project #5: YUI CSS and serving static media
   Django Blog Project #6: Creating standard blog views
   Django Blog Project #7: Adding a simple Atom feed
   Django Blog Project #8: Adding basic comment functionality


[Read the full post...]

How to get the current date and time in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/06/how-to-get-current-date-and-time-in/

You should be redirected in 2 seconds.



Here is an example of how to get the current date and time using the datetime module in Python:

import datetime

now = datetime.datetime.now()

print
print "Current date and time using str method of datetime object:"
print str(now)

print
print "Current date and time using instance attributes:"
print "Current year: %d" % now.year
print "Current month: %d" % now.month
print "Current day: %d" % now.day
print "Current hour: %d" % now.hour
print "Current minute: %d" % now.minute
print "Current second: %d" % now.second
print "Current microsecond: %d" % now.microsecond

print
print "Current date and time using strftime:"
print now.strftime("%Y-%m-%d %H:%M")

Results:
Current date and time using str method of datetime object:
2008-06-26 11:33:15.309236

Current date and time using instance attributes:
Current year: 2008
Current month: 6
Current day: 26
Current hour: 11
Current minute: 33
Current second: 15
Current microsecond: 309236

Current date and time using strftime:
2008-06-26 11:33


Directly from the time module documentation, here are more options to use with strftime:
Directive Meaning Notes
%a Locale's abbreviated weekday name.
%A Locale's full weekday name.
%b Locale's abbreviated month name.
%B Locale's full month name.
%c Locale's appropriate date and time representation.
%d Day of the month as a decimal number [01,31].
%H Hour (24-hour clock) as a decimal number [00,23].
%I Hour (12-hour clock) as a decimal number [01,12].
%j Day of the year as a decimal number [001,366].
%m Month as a decimal number [01,12].
%M Minute as a decimal number [00,59].
%p Locale's equivalent of either AM or PM. (1)
%S Second as a decimal number [00,61]. (2)
%U Week number of the year (Sunday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Sunday are considered to be in week 0. (3)
%w Weekday as a decimal number [0(Sunday),6].
%W Week number of the year (Monday as the first day of the week) as a decimal number [00,53]. All days in a new year preceding the first Monday are considered to be in week 0. (3)
%x Locale's appropriate date representation.
%X Locale's appropriate time representation.
%y Year without century as a decimal number [00,99].
%Y Year with century as a decimal number.
%Z Time zone name (no characters if no time zone exists).
%% A literal "%" character.


See also:
   5.1.4 datetime Objects in the 5.1 datetime module of the Python Library Reference.
[Read the full post...]

/usr/bin/python: bad interpreter: Permission denied error



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/05/usrbinpython-bad-interpreter-permission/

You should be redirected in 2 seconds.



I have a Python script, myscript.py with a #!/usr/bin/python shebang* at the top and tried to execute it on Ubuntu Linux using ./myscript.py. I got the following error message:
 
bash:  ./myscript.py: /usr/bin/python: bad interpreter: Permission  denied
 
Here are things to check:
  • The file should be executable (use chmod +x myscript.py)
  • The file shoud have Unix line endings
  • The file shouldn't be on a fat32 or ntfs filesystem. Apparently, bash can't handle scripts that are stored on fat32 or ntfs
* #!/usr/bin/env python would be the more portable shebang

[Read the full post...]

Django Blog Project #1: Creating a basic blog



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/05/django-new-blog-project/

You should be redirected in 2 seconds.



It's been a while since my last post on Django. I became very busy but also found the Django tutorial to be somewhat dry. Luckily, the official Django Book was published during this time and it is more interesting to read. This post will be a very brief summary of the first 6 chapters of the Django Book as I apply it towards the creation of a new blog website. I highly recommend reading the book (I forgot to mention it is available free online). Then after reading the first six chapters, I hope this post can serve as kind of a refresher on how to put everything together.

As I mentioned, I decided to create my own blog site as my first Django project. I know it is not the most original idea in the world, but I thought it would be useful and a good learning experience. The following steps are my first cut at my new blog (dubbed 0.0.1) and basically just document the basic concepts of Django without providing much actual functionality.

I develop a model, a template, and a view in accordance with Django's MTV (see also MVC) development pattern. The model is of a blog post (aptly named Post) and contains only one attribute, the post body data (i.e. the actual text of the post). I should add in other data such as the title, date, tags, etc. But in order to keep things simple this first time around, it just has the post body. The model is connected to a SQLite database and is updated using Django's excellent admin interface. Finally, the model data is combined with a very basic template which just displays a title (My New Blog Version 0.0.1), and all the blog post bodies separated by a <hr>. Like I said, it's not very useful at this point, but I think I understand the basic concepts and how to put everything together much better now. The next step in my Django development will be to create some more interesting templates and views and add more useful data like titles and dates.

I also have a couple of related plans:

  • Set up hosting: I've decided to use WebFaction for my hosting but I need to set up and upload my new, almost-website there. This will probably be the subject of my next Django post.
  • Copy my Blogger posts over to my new site. I've already figured out how to use Beautiful Soup to screen scrape my Blogger posts and import them into SQLite. Likely I will do this further on in the process.

Here are the steps I took for my first cut at my new blog. Note, I'm running on Ubuntu so that's why I have the $ bash prompt and use /home/sofeng paths in my examples.

Create a new project

The first thing to do after installing Django is to create a new project. Luckily, it takes just one command to create the project.

  1. Create a new project
    $ cd ~/src/django
    $ django-admin.py startproject myblogsite
  2. Take a look at the site using the development server
    $ python manage.py runserver
    Then go to http://127.0.0.1:8000
Set up the Django admin interface

At first I thought the admin interface was kind of boring. However, for my blog site, I will use the admin interface to enter new blog posts.

  1. Edit myblogsite/settings.py to add the admin application to the list of installed apps:
    INSTALLED_APPS = (
       'django.contrib.auth',
       'django.contrib.contenttypes',
       'django.contrib.sessions',
       'django.contrib.sites',
       'django.contrib.admin',
    )
  2. Install database tables for the admin interface:
    $ python manage.py syncdb
    At this point I was asked to create a superuser to log into the admin interface. I answered "yes" and filled in the appropriate information.
    Creating table auth_message
    Creating table auth_group
    Creating table auth_user
    Creating table auth_permission
    Creating table django_content_type
    Creating table django_session
    Creating table django_site
    Creating table django_admin_log
    
    You just installed Django's auth system, which means you don't have any superusers defined.
    Would you like to create one now? (yes/no): yes
    Username (Leave blank to use 'sofeng'): sofeng
    E-mail address: sofeng@sofeng.com
    Password:
    Password (again):
    Superuser created successfully.
    Installing index for auth.Message model
    Installing index for auth.Permission model
    Installing index for admin.LogEntry model
    Loading 'initial_data' fixtures...
    No fixtures found.
  3. Edit myblogsite/urls.py to include the admin url.
    from django.conf.urls.defaults import *
    
    urlpatterns = patterns('',
       (r'^admin/', include('django.contrib.admin.urls')),
    )
  4. Run the development server:
    $ python manage.py runserver
    Then go to http://127.0.0.1:8000/admin Log in and take a look around.
Set up the SQLite3 database

I chose SQLite because it is a lightweight, simple alternative to MySQL or PostgreSQL. This makes it great for a development website.

  1. Edit the following section in the myblogsite/settings.py file:
    DATABASE_ENGINE = 'sqlite3'
    DATABASE_NAME = '/home/sofeng/src/django/myblogsite/mydatabase.sqlite3'
    The rest of the DATABASE_ variables are not used with SQLite.
  2. Test out the database configuration: Run the shell:
    $ python manage.py shell
    Then type these commands:
    >>> from django.db import connection
    >>> cursor = connection.cursor()
    If nothing happens, all is good. See Table 5-2 in Chapter 5 of the Django Book common database configuration error messages.
Create an App

I think of an "app" as a piece of specific functionality of a website, whereas a project corresponds to a particular website. There can be many apps in a project. Also, apps can be used in more than one project. For more information about the differences between projects and apps see Chapter 5 of the Django Book.

  1. Create an app
    $ cd ~/src/django/myblogsite
    $ python manage.py startapp myblogapp
Create a Model

I created one model, the Post model. A model roughly corresponds to a SQL table. And each attribute in that model corresponds to a table row. I added the class Admin: so that my Post model would show up in the Admin interface (where I can insert the data).

  1. Edit myblogsite/myblogapp/models.py to look like the following:
    from django.db import models
    
    class Post(models.Model):
       body = models.TextField()
    
       # in the future I will add these other attributes
    #    title = models.CharField(maxlength=500)
    #    timestamp = models.CharField(maxlength=50)
    #    tags = models.CharField(maxlength=200)
    
       class Admin:
           pass
    Correction 7/6/2008: For the Post's body field, I previously used the line: body = models.CharField(maxlength=999999). However, thanks to Myles's comment in my post #4, I've changed this to use the more appropriate TextField.
Install the Model

After writing the Python model code, I needed to create the actual tables in the SQLite database. The following steps include a couple of checks, then I create the tables in the last step.

  1. Edit myblogsite/settings.py file again and add the blog app to the list of installed apps:
    INSTALLED_APPS = (
       'django.contrib.auth',
       'django.contrib.contenttypes',
       'django.contrib.sessions',
       'django.contrib.sites',
       'myblogsite.myblogapp',
    )
  2. Try validating the model:
    $ python manage.py validate
    Which gives the following message:
    0 errors found.
  3. Check the CREATE TABLE statements that Django will generate. Note, the database won't be modified.
    $ python manage.py sqlall myblogapp
    Which yields the following:
    BEGIN;
    CREATE TABLE "myblogapp_post" (
       "id" integer NOT NULL PRIMARY KEY,
       "body" text NOT NULL
    );
    COMMIT;
    Correction 7/6/2008: I've updated the results here to reflect the correction I made to the model above.
  4. Now, actually create the tables in SQLite:
    $ python manage.py syncdb
    Which yields something like this:
    Creating table blog_post
    Loading 'initial_data' fixtures...
    No fixtures found.
Create some new data using the admin interface

Now that I created the models and tied them to the admin interface, I can start adding data using the admin interface.

  1. Start the development server again:
    $ python manage.py runserver
    Go to http://127.0.0.1:8000/admin and log in.
  2. Under the "Blog" heading, click "Posts", then add some new posts using "Add post" and the "Save" links. This will add data to the SQLite database.
Create a template

Now I will display the data I just created using a template and a view. The template holds all the HTML code and some simple Django template code which the view's Python code uses to customize the page.

  1. Create the file myblogsite/templates/mytemplate.html and put the following inside:
    <html>
     <head><title>Post</title></head>
     <body>
       <h1>My New Blog Version 0.0.1</h1>
    
       {% for post in post_list %}
       {{ post }}
       <hr />
       {% endfor %}
    
     </body>
    </html>
  2. Edit myblogsite/settings.py again to instruct Django where to find the template files.
    TEMPLATE_DIRS = (
       '/home/sofeng/src/django/myblogsite/templates',
    )
    Be sure to include the comma at the end.
Create a view

The view is where I will grab the data from my model and insert it into my template.

  1. Create a new file myblogsite/myblogapp/views.py and put the following inside:
    from django.shortcuts import render_to_response
    from myblogsite.myblogapp.models import Post
    
    def myview(request):
       posts = Post.objects.all()
       post_body_list = [post.body for post in posts]
       return render_to_response('mytemplate.html',
                                 {'post_list': post_body_list})
    Correction 7/6/2008: I previously had from myblogapp.models import Post on the second line. This works, but is inconsistent with my urls.py below and can (and did for me) cause subtle errors in the future. I corrected the line to read: from myblogsite.myblogapp.models import Post.
Map an URL to the new view

Finally, I map an URL to my newly created view.

  1. Edit myblogsite/urls.py so that it looks like:
    from django.conf.urls.defaults import *
    from myblogsite.myblogapp.views import myview
    
    urlpatterns = patterns('',
       (r'^admin/', include('django.contrib.admin.urls')),
       (r'^myview/$', myview),
    )
  2. Take a look at the new page: Run the server:
    $ python manage.py runserver
    Then go to http://127.0.0.1:8000/myview Visiting the url shows all the posts I entered through the admin interface. Nice. Here is a snapshot screenshot of my new blog:

That's it for now. I tried to map out the basic steps for using Django's MTV development pattern. Hopefully, in the future, I'll be able to add more useful features to my new Django-powered blog.


Related posts:
   Install Django on Ubuntu
   Django Blog Project #2: Deploying at Webfaction
   Django Blog Project #3: Using CSS and Template Inheritance
   Django Blog Project #4: Adding post metadata
   Django Blog Project #5: YUI CSS and serving static media
   Django Blog Project #6: Creating standard blog views
   Django Blog Project #7: Adding a simple Atom feed
   Django Blog Project #8: Adding basic comment functionality


[Read the full post...]

How to use Python's enumerate and zip to iterate over two lists and their indices.



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/04/how-to-use-pythons-enumerate-and-zip-to/

You should be redirected in 2 seconds.



enumerate - Iterate over indices and items of a list
The Python Cookbook (Recipe 4.4) describes how to iterate over items and indices in a list using enumerate. For example:
alist = ['a1', 'a2', 'a3']

for i, a in enumerate(alist):
    print i, a
yields:
0 a1
1 a2
2 a3

zip - Iterate over two lists in parallel
I previously wrote about using zip to iterate over two lists in parallel. Example:
alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']

for a, b in zip(alist, blist):
    print a, b
yields:
a1 b1
a2 b2
a3 b3

enumerate with zip
Here is how to iterate over two lists and their indices using enumerate together with zip:
alist = ['a1', 'a2', 'a3']
blist = ['b1', 'b2', 'b3']

for i, (a, b) in enumerate(zip(alist, blist)):
    print i, a, b
yields:
0 a1 b1
1 a2 b2
2 a3 b3

[Read the full post...]

Recommended Books



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/04/recommended-books/

You should be redirected in 2 seconds.



I love having a subscription to Safari Books Online. Currently my company provides a free subscription, but if I get a new job, I might consider subscribing myself. Since I get to browse a number of books at no cost, I thought I'd note which books are my favorites. (Note, I am not being paid by Safari Books Online.)


General Software
  • Structure and Interpretation of Computer Programs, Second Edition, Harold Abelson and Gerald Jay Sussman, MIT Press, ?year?
    I learned about this book through a job posting. It might bring you to tears if you get it. I'm only in the second chapter. It is used in an introductory Computer Science course at MIT. It uses Scheme (Lisp) to demonstrate concepts.
    Available free online at: http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-4.html
    A free video lecture series is also available.

C
  • The C Programming Language, Second Edition, Brian W. Kernighan and Dennis M. Ritchie, Prentice Hall, 1988
    The definitive C book.

Python
  • Core Python Programming, Second Edition, Wesley J. Chun, Prentice Hall, September 18, 2006
    Usually I like O'Reilly books best, but I slightly prefer Chun's text to Learning Python.
    Available at Safari Books Online

Django (Python)
  • The Django Book, Apress, December 2007
    I think this is the first official Django book.
    Available free online at: http://www.djangobook.com/

SQLite
  • The Definitive Guide to SQLite, Mike Owens, Apress, May 2006
    I browsed a few SQL books but liked this one better than most. It has a good theory section.
    Available at Apress.com

Linux or related
  • X Power Tools, Chris Tyler, O'Reilly, December 15, 2007
    Lots of good information on the X Window System and more; easy to understand. I wish the basic Ubuntu or Linux books had some of this information.
    Available at Safari Books Online

Ruby
[Read the full post...]

Working with files and directories in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/04/working-with-files-and-directories-in/

You should be redirected in 2 seconds.



I often have a difficult time finding the appropriate functions to work with files and directories in Python. I think one reason is because the Library Reference puts seemingly related functions in two different places: 11 File and Directory Access and 14.1.4 Files and Directories.

Section 11, File and Directory Access contains documentation for useful functions such as os.path.exists which checks if a path exists, glob.glob which is useful for matching filenames using the Unix-style * and ? wildcards, and shutil.copy which is similary to the Unix cp command.

This section includes a total of 11 subsections for modules related to file and directory access, but it does not contain basic commands such as os.chdir, os.listdir, or os.rename. These functions are documented in Section 14.1.4, Files and Directories, instead.

Here is a summary of some of the functions I find useful. Check the documentation for more complete and detailed information.

11 File and Directory Access
  • os.path module:
    • exists - checks if a path or file exists
      Example:
      import os.path
      print os.path.exists("c:/Windows")
      Results:
      True
    • isfile and isdir - test if the path is a file or directory, respectively.
      Example:
      import os.path
      print os.path.isfile("c:/Windows")
      print os.path.isdir("c:/Windows")
      Results:
      False
      True
    • getmtime - returns the modification time of a path
      Example:
      import os.path
      import time
      mtime = os.path.getmtime("c:/Windows")
      print time.gmtime(mtime)
      Results:
      (2008, 4, 2, 15, 58, 39, 2, 93, 0)
  • glob module:
    • glob: returns a list of paths matching a Unix-style glob pattern.
      Example:
      import glob
      print glob.glob("c:/windows/*.bmp")
      Results:
      ['c:/windows\\Blue Lace 16.bmp', 'c:/windows\\Coffee Bean.bmp', 'c:/windows\\default.bmp', 'c:/windows\\FeatherTexture.bmp', 'c:/windows\\Gone Fishing.bmp', 'c:/windows\\Greenstone.bmp', 'c:/windows\\Prairie Wind.bmp', 'c:/windows\\Rhododendron.bmp', 'c:/windows\\River Sumida.bmp', 'c:/windows\\Santa Fe Stucco.bmp', 'c:/windows\\Soap Bubbles.bmp', 'c:/windows\\winnt.bmp', 'c:/windows\\winnt256.bmp', 'c:/windows\\Zapotec.bmp']
  • shutil module:
    • copy - similar to Unix cp
    • copy2 - similar to Unix cp -p
    • copytree - similar to Unix cp -r
    • rmtree - similar to Unix rm -r
14 Generic Operating System Services - > 14.1 os --- Miscellaneous operating system interfaces -> 14.1.4 Files and Directories
  • chdir - change the current working directory
  • getcwd - return a string representing the current working directory
  • listdir - return a list of the names of the entries in the directory.
  • makedir - create a directory
  • remove - remove a file (this is identical to unlink)
  • rename - rename the file or directory
  • walk - walks a directory tree (see my os.walk example)

[Read the full post...]

PyQt: How to pass arguments while emitting a signal



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/pyqt-how-to-pass-arguments-while/

You should be redirected in 2 seconds.



I often forget how to do this so I'm documenting it here for future reference. If I want to emit a signal and also pass an argument with that signal, I can use the form self.emit(SIGNAL("mySignalName"), myarg). I connect the signal to a method in the usual way. To use the argument, I merely need to specify the argument in the method definition. What often confuses me is that I don't need to specify arguments in the connect statement. The example below emits a signal didSomething and passes two arguments, "important" and "information" to the update_label method.

import sys
import time
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

#################################################################### 
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args)

        self.label = QLabel(" ")
        layout = QVBoxLayout()
        layout.addWidget(self.label)
        self.setLayout(layout)
        self.connect(self, SIGNAL("didSomething"),
                     self.update_label)
        self.do_something()

    def do_something(self):
        self.emit(SIGNAL("didSomething"), "important", "information")

    def update_label(self, value1, value2):
        self.label.setText(value1 + " " + value2)

####################################################################
if __name__ == "__main__": 
    app = QApplication(sys.argv) 
    w = MyWindow() 
    w.show() 
    sys.exit(app.exec_())

[Read the full post...]

PyQt4 QItemDelegate example with QListView and QAbstractListModel



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/pyqt4-qitemdelegate-example-with/

You should be redirected in 2 seconds.



I am currently working on a mini project which uses a QListView to display items in a list box. I am happy with most of the default behavior in the list view, however, I want to change how the highlighting of selected items is displayed. Currently, in my Windows environment, selecting an item in the list highlights the item in dark blue. This is fine, however, when the list box loses focus, the highlight color turns to a light gray, which is hard for me to see. I would like the selection to have a red highlight, whether the widget has focus or not.

My solution is to add a custom delegate to my list view. Normally, a standard view uses a default delegate (QItemDelegate) to render and edit the model's data. To customize the way the data is displayed in the view, I subclass QItemDelegate and implement a custom paint() method to set the background color to red for selected items. (Note, it is possible to specify certain formatting (including background color) using ItemDataRoles in the QAbstractListModel subclass, however, using a custom delegate is more powerful, and I didn't want to mix appearance-related code with my data model.)

In the example below, I started with the simple QListView / QAbstractListModel example, and added MyDelegate, a subclass of QItemDelegate. This class reimplements the paint() method to highlight selected items in red.

See also: Qt 4.3 QItemDelegate documentation

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

####################################################################
def main():
    app = QApplication(sys.argv)
    w = MyWindow()
    w.show()
    sys.exit(app.exec_())

####################################################################
class MyWindow(QWidget):
    def __init__(self, *args):
        QWidget.__init__(self, *args)

        # create objects
        list_data = [1,2,3,4]
        lm = MyListModel(list_data, self)
        de = MyDelegate(self)
        lv = QListView()
        lv.setModel(lm)
        lv.setItemDelegate(de)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(lv)
        self.setLayout(layout)

####################################################################
class MyDelegate(QItemDelegate):
    def __init__(self, parent=None, *args):
        QItemDelegate.__init__(self, parent, *args)

    def paint(self, painter, option, index):
        painter.save()

        # set background color
        painter.setPen(QPen(Qt.NoPen))
        if option.state & QStyle.State_Selected:
            painter.setBrush(QBrush(Qt.red))
        else:
            painter.setBrush(QBrush(Qt.white))
        painter.drawRect(option.rect)

        # set text color
        painter.setPen(QPen(Qt.black))
        value = index.data(Qt.DisplayRole)
        if value.isValid():
            text = value.toString()
            painter.drawText(option.rect, Qt.AlignLeft, text)

        painter.restore()

####################################################################
class MyListModel(QAbstractListModel):
    def __init__(self, datain, parent=None, *args):
        """ datain: a list where each item is a row
        """
        QAbstractTableModel.__init__(self, parent, *args)
        self.listdata = datain

    def rowCount(self, parent=QModelIndex()):
        return len(self.listdata)

    def data(self, index, role):
        if index.isValid() and role == Qt.DisplayRole:
            return QVariant(self.listdata[index.row()])
        else:
            return QVariant()

####################################################################
if __name__ == "__main__":
    main()

[Read the full post...]

Python examples / recipes / howto's



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/python-examples-recipes-howtos/

You should be redirected in 2 seconds.




finditer regular expression examples
dict and list examples
misc python examples
matplotlib examples
Django examples
[Read the full post...]

How to invert a dict in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/how-to-invert-dict-in-python/

You should be redirected in 2 seconds.



Example 1: If the values in the dictionary are unique and hashable, then I can use Recipe 4.14 in the Python Cookbook, 2nd Edition.

def invert_dict(d):
    return dict([(v, k) for k, v in d.iteritems()])

d = {'child1': 'parent1',
     'child2': 'parent2',
     }
print invert_dict(d)
{'parent2': 'child2', 'parent1': 'child1'}

Example 2: If the values in the dictionary are hashable, but not unique, I can create a dict of lists as an inverse.

def invert_dict_nonunique(d):
    newdict = {}
    for k, v in d.iteritems():
        newdict.setdefault(v, []).append(k)
    return newdict

d = {'child1': 'parent1',
     'child2': 'parent1',
     'child3': 'parent2',
     'child4': 'parent2',
     }
print invert_dict_nonunique(d)
{'parent2': ['child3', 'child4'], 'parent1': ['child1', 'child2']}

Example 3: If I am starting with a dict of lists, where lists contain unique hashable items, I can create an inverse as shown below.

def invert_dol(d):
    return dict((v, k) for k in d for v in d[k])

d = {'child1': ['parent1'],
     'child2': ['parent2', 'parent3'],
     }
print invert_dol(d)
{'parent3': 'child2', 'parent2': 'child2', 'parent1': 'child1'}

Example 4: If I am starting with a dict of lists, where lists contain non-unique hashable items, I can create another dict of lists as an inverse.

def invert_dol_nonunique(d):
    newdict = {}
    for k in d:
        for v in d[k]:
            newdict.setdefault(v, []).append(k)
    return newdict

d = {'child1': ['parent1'],
     'child2': ['parent1'],
     'child3': ['parent2'],
     'child4': ['parent2'],
     'child5': ['parent1', 'parent2'],
     }
print invert_dol_nonunique(d)
{'parent2': ['child3', 'child4', 'child5'], 'parent1': ['child1', 'child2', 'child5']}

[Read the full post...]

Notes on Python variable scope



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/python-variable-scope-notes/

You should be redirected in 2 seconds.



Example 1: The difference between global and local variables
Global variables are accessible inside and outside of functions. Local variables are only accessible inside the function. In the example below, the function can access both the global and the local variable. However, trying to access the local variable outside the function produces an error.

global_var = 'foo'
def ex1():
    local_var = 'bar'
    print global_var
    print local_var

ex1()
print global_var
print local_var  # this gives an error
foo
bar
foo
Traceback (most recent call last):
  File "nested_scope.py", line 12, in 
    print local_var  # this gives an error
NameError: name 'local_var' is not defined

Example 2: How *not* to set a global variable
*Setting* a global variable from within a function is not as simple. If I set a variable in a function with the same name as a global variable, I am actually creating a new local variable. In the example below, var remains 'foo' even after the function is called.

var = 'foo'
def ex2():
    var = 'bar'
    print 'inside the function var is ', var

ex2()
print 'outside the function var is ', var
inside the function var is  bar
outside the function var is  foo

Example 3: How to set a global variable
To set the global variable inside a function, I need to use the global statement. This declares the inner variable to have module scope. Now var remains 'bar' after the function is called.

var = 'foo'
def ex3():
    global var
    var = 'bar'
    print 'inside the function var is ', var

ex3()
print 'outside the function var is ', var
inside the function var is  bar
outside the function var is  bar

Example 4: Nested functions
Scoping for nested functions works similarly. In the example below, the inner function can access both var_outer and var_inner. However, the outer function cannot access var_inner. Side note: the inner function is considered a closure if it makes reference to a non-global outside variable.

def ex4():
    var_outer = 'foo'
    def inner():
        var_inner = 'bar'
        print var_outer
        print var_inner
    inner()
    print var_outer
    print var_inner # this gives an error

ex4()
foo
bar
foo
Traceback (most recent call last):
  File "nested_scope.py", line 53, in 
    ex3()
  File "nested_scope.py", line 51, in ex3
    print var_inner # this gives an error
NameError: global name 'var_inner' is not defined

Example 5: How *not* to set an outer variable
Like Example 2, setting a variable in the inner function creates a new local variable instead of modifying the outer variable. In the example below, var in the outer function does not get changed to 'bar'.

def ex5():
    var = 'foo'
    def inner():
        var = 'bar'
        print 'inside inner, var is ', var
    inner()
    print 'inside outer function, var is ', var

ex5()
inside inner, var is  bar
inside outer function, var is  foo

Example 6: Another way to *not* set an outer variable
However, using the global keyword won't work in this case. global cause a variable to have module scope, but I want my variable to have the scope of the outer function. Per the Python 3000 Status Update, Python 3000 will have a nonlocal keyword to solve this problem. See PEP 3104 for more information about nonlocal and nested scopes. In the example below, var is still not changed to 'bar' in the outer function.

def ex6():
    var = 'foo'
    def inner():
        global var
        var = 'bar'
        print 'inside inner, var is ', var
    inner()
    print 'inside outer function, var is ', var

ex6()
inside inner, var is  bar
inside outer function, var is  foo

Example 7: A workaround until Python 3000 arrives
A workaround is to create an additional namespace. Now the variable in the outer function can be set to 'bar'.

class Namespace: pass
def ex7():
    ns = Namespace()
    ns.var = 'foo'
    def inner():
        ns.var = 'bar'
        print 'inside inner, ns.var is ', ns.var
    inner()
    print 'inside outer function, ns.var is ', ns.var
ex7()
inside inner, ns.var is  bar
inside outer function, ns.var is  bar

Reference: Core Python Programming, Second Edition, Ch 11


[Read the full post...]

Saving a Python dict to a file using pickle



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/saving-python-dict-to-file-using-pickle/

You should be redirected in 2 seconds.



Per Programming Python, 3rd Edition, there are a number of methods to store persistent data with Python:

  • I often use flat files to read or write text (string) data using the os library.
  • Flat files are read sequentially, but dbm files allow for keyed access to string data
  • The pickle module can be used to store non-string Python data structures, such as Python dicts. However, the data is not keyed as with dbm files.
  • shelve files combine the best of the dbm and pickle methods by storing pickled objects in dbm keyed files.
  • I've read good things about the ZODB object-oriented database , but I don't know too much about it. Per the book, it is a more powerful alternative to shelves.
  • The final option is interfacing with a full-fledged SQL relational databases. As I mentioned before, Python 2.5 has an interface to SQLite as part of the standard distribution.

Here is an example using pickle which writes a Python dict to a file and reads it back again:

import pickle

# write python dict to a file
mydict = {'a': 1, 'b': 2, 'c': 3}
output = open('myfile.pkl', 'wb')
pickle.dump(mydict, output)
output.close()

# read python dict back from the file
pkl_file = open('myfile.pkl', 'rb')
mydict2 = pickle.load(pkl_file)
pkl_file.close()

print mydict
print mydict2

Results:
{'a': 1, 'c': 3, 'b': 2}
{'a': 1, 'c': 3, 'b': 2}

[Read the full post...]

Python PyQt Tab Completion example



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/python-pyqt-tab-completion-example/

You should be redirected in 2 seconds.



Here is an example Python GUI that implements tab completion. It uses the open source Qt 4.3 toolkit and PyQt 4.3 Python bindings.

A list of words is presented in a list box. As the user types, the list is shortened to show possible matches. If the user presses TAB, the input text is "completed" to the longest possible string match. This may be a whole word or a common substring of multiple words.

This example consists of two basic elements:

  • MyLineEdit is a subclass of the QLineEdit class. It is used as an input box to enter text. I needed to subclass QLineEdit because I needed to capture the TAB key press event for tab completion. (See this previous post.)
  • QListView and MyListModel implement a list with a simple model/view architechture. MyListModel is a subclass of QAbstractListModel. I implemented the required rowCount and data methods as well as a method called setAllData which replaces the entire existing data with a new list of data.

This example makes use of two SIGNALs:

  • The textChanged signal is emitted each time the user types a letter inside the QLineEdit box. It is connected to the text_changed method which updates the list of words in the QListView. MyListModel's setAllData method is used to update the data.
  • The tabPressed signal is a custom signal I added to my QLineEdit subclass. It is emitted each time the user presses the TAB key. This signal is connected the tab_pressed method which completes the input to the longest matching substring of the available words.

import sys
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

LIST_DATA = ['a', 'aardvark', 'aardvarks', 'aardwolf', 'aardwolves',
             'abacus', 'babel', 'bach', 'cache', 
             'daggle', 'facet', 'kabob', 'kansas']

#################################################################### 
def main(): 
    app = QApplication(sys.argv) 
    w = MyWindow() 
    w.show() 
    sys.exit(app.exec_()) 

#################################################################### 
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args) 

        # create objects
        self.la = QLabel("Start typing to match items in list:")
        self.le = MyLineEdit()
        self.lm = MyListModel(LIST_DATA, self)
        self.lv = QListView()
        self.lv.setModel(self.lm)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(self.la)
        layout.addWidget(self.le)
        layout.addWidget(self.lv) 
        self.setLayout(layout)

        # connections
        self.connect(self.le, SIGNAL("textChanged(QString)"),
                     self.text_changed)
        self.connect(self.le, SIGNAL("tabPressed"),
                     self.tab_pressed)

    def text_changed(self):
        """ updates the list of possible completions each time a key is 
            pressed """
        pattern = str(self.le.text())
        self.new_list = [item for item in LIST_DATA if item.find(pattern) == 0]
        self.lm.setAllData(self.new_list)

    def tab_pressed(self):
        """ completes the word to the longest matching string 
            when the tab key is pressed """

        # only one item in the completion list
        if len(self.new_list) == 1:
            newtext = self.new_list[0] + " "
            self.le.setText(newtext)

        # more than one remaining matches
        elif len(self.new_list) > 1:
            match = self.new_list.pop(0)
            for word in self.new_list:
                match = string_intersect(word, match)
            self.le.setText(match)

####################################################################
class MyLineEdit(QLineEdit):
    def __init__(self, *args):
        QLineEdit.__init__(self, *args)
        
    def event(self, event):
        if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_Tab):
            self.emit(SIGNAL("tabPressed"))
            return True
        return QLineEdit.event(self, event)

#################################################################### 
class MyListModel(QAbstractListModel): 
    def __init__(self, datain, parent=None, *args): 
        """ datain: a list where each item is a row
        """
        QAbstractTableModel.__init__(self, parent, *args) 
        self.listdata = datain
 
    def rowCount(self, parent=QModelIndex()): 
        return len(self.listdata) 
 
    def data(self, index, role): 
        if index.isValid() and role == Qt.DisplayRole:
            return QVariant(self.listdata[index.row()])
        else: 
            return QVariant()

    def setAllData(self, newdata):
        """ replace all data with new data """
        self.listdata = newdata
        self.reset()

####################################################################
def string_intersect(str1, str2):
    newlist = []
    for i,j in zip(str1, str2):
        if i == j:
            newlist.append(i)
        else:
            break
    return ''.join(newlist)

####################################################################
if __name__ == "__main__": 
    main()

[Read the full post...]

How to use *args and **kwargs in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/how-to-use-args-and-kwargs-in-python/

You should be redirected in 2 seconds.



Or, How to use variable length argument lists in Python.

The special syntax, *args and **kwargs in function declarations is used to pass a variable number of arguments to a function. The single asterisk form (*args) is used to pass a non-keyworded, variable-length argument list, and the double asterisk form is used to pass a keyworded, variable-length argument list. Here is an example of how to use the non-keyworded form. This example passes one formal argument, and two more variable length arguments.

def test_var_args(farg, *args):
    print "formal arg: %s" % farg
    for arg in args:
        print "another arg: %s" % arg

test_var_args(1, 'two', 3)

Results:
formal arg: 1
another arg: two
another arg: 3

Here is an example of how to use the keyworded form. Again, one formal argument and two keyworded variable arguments are passed.

def test_var_kwargs(farg, **kwargs):
    print "formal arg: %s" % farg
    for key in kwargs:
        print "another keyword arg, %s: %s" % (key, kwargs[key])

test_var_kwargs(farg=1, myarg2='two', myarg3=3)

Results:
formal arg: 1
another keyword arg, myarg2: two
another keyword arg, myarg3: 3

See also Section 5.3.4 in the Python Reference Manual

Reference: Core Python Programming, Second Edition, Section 11.6

[Read the full post...]

How to find the intersection and union of two lists in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/how-to-find-intersection-and-union-of/

You should be redirected in 2 seconds.



My friend Bill had previously alerted me to the coolness of Python sets. However I hadn't found opportunity to use them until now. Here are three functions using sets to remove duplicate entries from a list, find the intersection of two lists, and find the union of two lists. Note, sets were introduced in Python 2.4, so Python 2.4 or later is required. Also, the items in the list must be hashable and order of the lists is not preserved.

For more information on Python sets, see the Library Reference.

""" NOTES:
      - requires Python 2.4 or greater
      - elements of the lists must be hashable
      - order of the original lists is not preserved
"""
def unique(a):
    """ return the list with duplicate elements removed """
    return list(set(a))

def intersect(a, b):
    """ return the intersection of two lists """
    return list(set(a) & set(b))

def union(a, b):
    """ return the union of two lists """
    return list(set(a) | set(b))

if __name__ == "__main__": 
    a = [0,1,2,0,1,2,3,4,5,6,7,8,9]
    b = [5,6,7,8,9,10,11,12,13,14]
    print unique(a)
    print intersect(a, b)
    print union(a, b)

Results:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[8, 9, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]

[Read the full post...]

How to capture the Tab key press event with PyQt 4.3



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/how-to-capture-tab-key-press-event-with/

You should be redirected in 2 seconds.



Normally, pressing the TAB key changes focus among widgets. However, I would like to use the TAB key for other purposes (e.g. tab completion). To gain control of the TAB key press event, I need to subclass my widget and reimplement the QObject.event() event handler. I don't need to re-write the entire event handler. I only need to process TAB key press events. I will pass all other events to the default event handler. The example below subclasses the QLineEdit widget and reimplements the event() method. Pressing the TAB key inside this new widget prints out the text "tab pressed" inside a second QLineEdit box.

The Events and Event Filters Trolltech QT documentation has a good explanation of how this works. My example shows how to use Python and PyQt instead of C++.

import sys
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

#################################################################### 
def main(): 
    app = QApplication(sys.argv) 
    w = MyWindow() 
    w.show() 
    sys.exit(app.exec_()) 

####################################################################
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args)

        # create objects
        self.la = QLabel("Press tab in this box:")
        self.le = MyLineEdit()
        self.la2 = QLabel("\nLook here:")
        self.le2 = QLineEdit()

        # layout
        layout = QVBoxLayout()
        layout.addWidget(self.la)
        layout.addWidget(self.le)
        layout.addWidget(self.la2)
        layout.addWidget(self.le2)
        self.setLayout(layout)

        # connections
        self.connect(self.le, SIGNAL("tabPressed"),
                     self.update)

    def update(self):
        newtext = str(self.le2.text()) + "tab pressed "
        self.le2.setText(newtext)

####################################################################
class MyLineEdit(QLineEdit):
    def __init__(self, *args):
        QLineEdit.__init__(self, *args)
        
    def event(self, event):
        if (event.type()==QEvent.KeyPress) and (event.key()==Qt.Key_Tab):
            self.emit(SIGNAL("tabPressed"))
            return True

        return QLineEdit.event(self, event)

####################################################################
if __name__ == "__main__": 
    main()

[Read the full post...]

PyQt 4.3 Simple QAbstractListModel/ QlistView example



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2008/01/pyqt-43-simple-qabstractlistmodel/

You should be redirected in 2 seconds.



import sys
from PyQt4.QtCore import * 
from PyQt4.QtGui import * 

#################################################################### 
def main(): 
    app = QApplication(sys.argv) 
    w = MyWindow() 
    w.show() 
    sys.exit(app.exec_()) 

#################################################################### 
class MyWindow(QWidget): 
    def __init__(self, *args): 
        QWidget.__init__(self, *args) 

        # create table
        list_data = [1,2,3,4]
        lm = MyListModel(list_data, self)
        lv = QListView()
        lv.setModel(lm)

        # layout
        layout = QVBoxLayout()
        layout.addWidget(lv) 
        self.setLayout(layout)

#################################################################### 
class MyListModel(QAbstractListModel): 
    def __init__(self, datain, parent=None, *args): 
        """ datain: a list where each item is a row
        """
        QAbstractTableModel.__init__(self, parent, *args) 
        self.listdata = datain
 
    def rowCount(self, parent=QModelIndex()): 
        return len(self.listdata) 
 
    def data(self, index, role): 
        if index.isValid() and role == Qt.DisplayRole:
            return QVariant(self.listdata[index.row()])
        else: 
            return QVariant()

####################################################################
if __name__ == "__main__": 
    main()

[Read the full post...]

How to pass command line arguments to your Python program



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2007/12/how-to-pass-command-line-arguments-to/

You should be redirected in 2 seconds.



Here is an example for quick reference. argv holds the program name at index 0. That's why we start at 1.

#!/usr/bin/python

import sys

def main():
    # print command line arguments
    for arg in sys.argv[1:]:
        print arg

if __name__ == "__main__":
    main()

Try it out:
$ python cmdline_args.py arg1 arg2 arg3
arg1
arg2
arg3

See also:
sys module documentation
getopt module documentation
Guido van Rossum's post

[Read the full post...]

Tabular data structure conversion in Python



This is my OLD blog. I've copied this post over to my NEW blog at:

http://www.saltycrane.com/blog/2007/12/tabular-data-structure-conversion-in/

You should be redirected in 2 seconds.



Here is a Python libary to convert between various tabular data structures including list of lists, list of dicts, dict of lists, and dict of dicts. My original attempts at these conversions required that the data be rectangular (e.g. each column has the same number of elements). However, further research led me to this ASPN Recipe which uses map to transpose a list of lists even if it is not rectangular. With help from the mailing list, I rewrote the recipe without using lambda. (I did this because Guido suggested not to use map with lambda for the sake of clarity.)

The table below lists all the conversions available between the 8 types of tabular data structures. The function names link to the function definition below. I used list comprehensions wherever possible and a functional/ declarative approach in general. It is likely there is a better way to do many of these conversions. (After all, I just learned how to use zip().) In particular, the functions with the comment "Better way?" use a number of the other conversion functions in series to achieve the desired result. All of these could be optimized. Feedback on better methods is welcome.

    TO
    lorl
list of lists
where each inner list is a row
locl
list of lists
where each inner list is a column
lord
list of dicts
where each dict is a row
locd
list of dicts
where each dict is a column
dorl
dict of lists
where each list is a row
docl
dict of lists
where each list is a column
dord
dict of lists
where each inner dict is a row
docd
dict of lists
where each inner dict is a column
F
R
O
M
lorl
list of lists
where each inner list is a row
lorl2 locl() lorl2 lord() lorl2 locd() lorl2 dorl() lorl2 docl() lorl2 dord() lorl2 docd()
locl
list of lists
where each inner list is a column
locl2 lorl() locl2 lord() locl2 locd() locl2 dorl() locl2 docl() locl2 dord() locl2 docd()
lord
list of dicts
where each dict is a row
lord2 lorl() lord2 locl() lord2 locd() lord2 dorl() lord2 docl() lord2 dord() lord2 docd()
locd
list of dicts
where each dict is a column
locd2 lorl() locd2 locl() locd2 lord() locd2 dorl() locd2 docl() locd2 dord() locd2 docd()
dorl
dict of lists
where each list is a row
dorl2 lorl() dorl2 locl() dorl2 lord() dorl2 locd() dorl2 docl() dorl2 dord() dorl2 docd()
docl
dict of lists
where each list is a column
docl2 lorl() docl2 locl() docl2 lord() docl2 locd() docl2 dorl() docl2 dord() docl2 docd()
dord
dict of dicts
where each inner dict is a row
dord2 lorl() dord2 locl() dord2 lord() dord2 locd() dord2 dorl() dord2 docl() dord2 docd()
docd
dict of dicts
where each inner dict is a column
dord2 lorl() dord2 locl() dord2 lord() dord2 locd() dord2 dorl() dord2 docl() dord2 dord()

Example data structures

Here are examples of the 8 different tabular data structures. Note that if a transpose is performed (i.e. rows switched with columns or vice versa), the output is padded with None. Otherwise, it is left as is.

# lorl- list of lists where each inner list is a row
lorl = [
    ['a1', 'b1', 'c1'],    # row 1
    ['a2', 'b2', 'c2'],    # row 2
    ['a3', 'b3', 'c3'],    # row 3
    ['a4', 'b4',     ],    # row 4
    ]

# locl- list of lists where each inner list is a column
locl = [
    ['a1', 'a2', 'a3', 'a4'],    # col a
    ['b1', 'b2', 'b3', 'b4'],    # col b
    ['c1', 'c2', 'c3',     ],    # col c
    ]

# lord- list of dicts where each dict is a row
lord = [
    {'a':'a1', 'b':'b1', 'c':'c1'},   # row 1
    {'a':'a2', 'b':'b2', 'c':'c2'},   # row 2
    {'a':'a3', 'b':'b3', 'c':'c3'},   # row 3
    {'a':'a4', 'b':'b4',         },   # row 4
    ]

# locd- list of dicts where each dict is a column
locd = [
    {1:'a1', 2:'a2', 3:'a3', 4:'a4'},         # col a
    {1:'b1', 2:'b2', 3:'b3', 4:'b4'},         # col b
    {1:'c1', 2:'c2', 3:'c3',       },         # col c
    ]

# dorl- dict of lists where each list is a row
dorl = {
    1: ['a1', 'b1', 'c1'],            # row 1
    2: ['a2', 'b2', 'c2'],            # row 2
    3: ['a3', 'b3', 'c3'],            # row 3
    4: ['a4', 'b4',     ],            # row 4
    }
# docl- dict of lists where each list is a column
docl = {
    'a': ['a1', 'a2', 'a3', 'a4'],          # column a
    'b': ['b1', 'b2', 'b3', 'b4'],          # column b
    'c': ['c1', 'c2', 'c3',     ],          # column c
    }

# dord- dict of dicts where each inner dict is a row
dord = {
    1: {'a':'a1', 'b':'b1', 'c':'c1'},  # row 1
    2: {'a':'a2', 'b':'b2', 'c':'c2'},  # row 2
    3: {'a':'a3', 'b':'b3', 'c':'c3'},  # row 3
    4: {'a':'a4', 'b':'b4',         },  # row 4
    }

# docd- dict of dicts where each inner dict is a column
docd = {
    'a': {1:'a1', 2:'a2', 3:'a3', 4:'a4'},    # column a
    'b': {1:'b1', 2:'b2', 3:'b3', 4:'b4'},    # column b
    'c': {1:'c1', 2:'c2', 3:'c3',       },    # column c
    }

# list of row keys and column keys
rowkeys = [1, 2, 3, 4]
colkeys = ['a', 'b', 'c']

Code

Below is the libary of functions.

#!/usr/bin/python

"""tabular.py
Functions to convert tabular data structures

The following data structures are supported:
lorl- list of lists where each inner list is a row
locl- list of lists where each inner list is a column
lord- list of dicts where each dict is a row
locd- list of dicts where each dict is a column
dorl- dict of lists where each list is a row
docl- dict of lists where each list is a column
dord- dict of dicts where each inner dict is a row
docd- dict of dicts where each inner dict is a column
"""

#-------------------------------------------------------
# from lorl to ...
#-------------------------------------------------------
def lorl2locl(lorl):
    return [list(col) for col in map(None, *lorl)]

def lorl2lord(lorl, colkeys):
    return [dict(zip(colkeys, row)) for row in lorl]

def lorl2locd(lorl, rowkeys):
    # better way?
    return locl2locd(lorl2locl(lorl), rowkeys)

def lorl2dorl(lorl, rowkeys):
    return dict(zip(rowkeys, [row for row in lorl]))

def lorl2docl(lorl, colkeys):
    # better way?
    return locl2docl(lorl2locl(lorl), colkeys)

def lorl2dord(lorl, rowkeys, colkeys):
    return dict(zip(rowkeys, [dict(zip(colkeys, row)) 
                              for row in lorl]))

def lorl2docd(lorl, rowkeys, colkeys):
    # better way?
    return dict(zip(colkeys, [dict(zip(rowkeys, col))
                              for col in lorl2locl(lorl)]))

#-------------------------------------------------------
# from locl to ...
#-------------------------------------------------------
def locl2lorl(locl):
    return [list(row) for row in map(None, *locl)]

def locl2lord(locl, colkeys):
    # better way?
    return lorl2lord(locl2lorl(locl), colkeys)

def locl2locd(locl, rowkeys):
    return [dict(zip(rowkeys, col)) for col in locl]

def locl2dorl(locl, rowkeys):
    # better way?
    return dict(zip(rowkeys, [row for row in locl2lorl(locl)]))

def locl2docl(locl, colkeys):
    return dict(zip(colkeys, locl))

def locl2dord(locl, rowkeys, colkeys):
    # better way?
    return dict(zip(rowkeys, [dict(zip(colkeys, row))
                              for row in locl2lorl(locl)]))

def locl2docd(locl, rowkeys, colkeys):
    return dict(zip(colkeys, [dict(zip(rowkeys, col)) 
                              for col in locl]))

#-------------------------------------------------------
# from lord to ...
#-------------------------------------------------------
def lord2lorl(lord, colkeys):
    return [[row[key] for key in colkeys if key in row]
            for row in lord]

def lord2locl(lord, colkeys):
    # better way?
    return lorl2locl(lord2lorl(lord, colkeys))

def lord2locd(lord, rowkeys, colkeys):
    return [dict([(rkey, row[ckey])
                  for rkey, row in zip(rowkeys, lord) if ckey in row])
            for ckey in colkeys]

def lord2dorl(lord, rowkeys, colkeys):
    return dict(zip(rowkeys, [[row[ckey]
                               for ckey in colkeys if ckey in row]
                              for row in lord]))

def lord2docl(lord, colkeys):
    return dict(zip(colkeys, [[row[ckey]
                               for row in lord if ckey in row]
                              for ckey in colkeys]))

def lord2dord(lord, rowkeys):
    return dict(zip(rowkeys, lord))

def lord2docd(lord, rowkeys, colkeys):
    return dict(zip(colkeys,
                    [dict(zip(rowkeys,
                              [row[ckey]
                               for row in lord if ckey in row]))
                     for ckey in colkeys]))

#-------------------------------------------------------
# from locd to ...
#-------------------------------------------------------
def locd2lorl(locd, rowkeys):
    # better way?
    return locl2lorl(locd2locl(locd, rowkeys))

def locd2locl(locd, rowkeys):
    return [[col[key] for key in rowkeys if key in col]
            for col in locd]

def locd2lord(locd, rowkeys, colkeys):
    return [dict([(ckey, col[rkey])
                  for ckey, col in zip(colkeys, locd) if rkey in col])
            for rkey in rowkeys]

def locd2dorl(locd, rowkeys):
    return dict(zip(rowkeys, [[col[rkey]
                               for col in locd if rkey in col]
                              for rkey in rowkeys]))

def locd2docl(locd, rowkeys, colkeys):
    return dict(zip(colkeys, [[col[rkey]
                               for rkey in rowkeys if rkey in col]
                              for col in locd]))

def locd2dord(locd, rowkeys, colkeys):
    return dict(zip(rowkeys,
                    [dict(zip(colkeys,
                              [col[rkey]
                               for col in locd if rkey in col]))
                     for rkey in rowkeys]))

def locd2docd(locd, colkeys):
    return dict(zip(colkeys, locd))

#-------------------------------------------------------
# from dorl to ...
#-------------------------------------------------------
def dorl2lorl(dorl, rowkeys):
    return [dorl[key] for key in rowkeys]

def dorl2locl(dorl, rowkeys):
    # better way?
    return lorl2locl(dorl2lorl(dorl, rowkeys))

def dorl2lord(dorl, rowkeys, colkeys):
    return [dict(zip(colkeys, dorl[rkey]))
            for rkey in rowkeys]

def dorl2locd(dorl, rowkeys):
    # better way?
    return locl2locd(lorl2locl(dorl2lorl(dorl, rowkeys)), rowkeys)

def dorl2docl(dorl, rowkeys, colkeys):
    # better way?
    return locl2docl(lorl2locl(dorl2lorl(dorl, rowkeys)), colkeys)

def dorl2dord(dorl, rowkeys, colkeys):
    # better way?
    return lorl2dord(dorl2lorl(dorl, rowkeys), rowkeys, colkeys)

def dorl2docd(dorl, rowkeys, colkeys):
    # better way?
    return locl2docd(lorl2locl(dorl2lorl(dorl, rowkeys)),
                     rowkeys, colkeys)

#-------------------------------------------------------
# from docl to ...
#-------------------------------------------------------
def docl2lorl(docl, colkeys):
    # better way?
    return locl2lorl(docl2locl(docl, colkeys))

def docl2locl(docl, colkeys):
    return [docl[key] for key in colkeys]

def docl2lord(docl, rowkeys, colkeys):
    # better way?
    return lorl2lord(locl2lorl(docl2locl(docl, colkeys)), colkeys)

def docl2locd(docl, rowkeys, colkeys):
    #
    return [dict(zip(rowkeys, docl[ckey]))
            for ckey in colkeys]

def docl2dorl(docl, rowkeys, colkeys):
    # better way?
    return lorl2dorl(locl2lorl(docl2locl(docl, colkeys)), rowkeys)

def docl2dord(docl, rowkeys, colkeys):
    # better way?
    return lorl2dord(locl2lorl(docl2locl(docl, colkeys)),
                     rowkeys, colkeys)

def docl2docd(docl, rowkeys, colkeys):
    # better way?
    return locl2docd(docl2locl(docl, colkeys), rowkeys, colkeys)

#-------------------------------------------------------
# from dord to ...
#-------------------------------------------------------
def dord2lorl(dord, rowkeys, colkeys):
    return [[dord[rkey][ckey]
             for ckey in colkeys if ckey in dord[rkey]]
            for rkey in rowkeys if rkey in dord]

def dord2locl(dord, rowkeys, colkeys):
    # better way?
    return lorl2locl(dord2lorl(dord, rowkeys, colkeys))

def dord2lord(dord, rowkeys):
    return [dord[rkey] for rkey in rowkeys]

def dord2locd(dord, rowkeys, colkeys):
    # better way?
    return lord2locd(dord2lord(dord, rowkeys), rowkeys, colkeys)

def dord2dorl(dord, rowkeys, colkeys):
    # don't need zip
    return dict([(rkey, [dord[rkey][ckey]
                         for ckey in colkeys if ckey in dord[rkey]])
                 for rkey in rowkeys])

def dord2docl(dord, rowkeys, colkeys):
    # better way?
    return locl2docl(lorl2locl(dord2lorl(dord, rowkeys, colkeys)),
                     colkeys)

def dord2docd(dord, rowkeys, colkeys):
    # better way?
    return locl2docd(lorl2locl(dord2lorl(dord, rowkeys, colkeys)),
                     rowkeys, colkeys)

#-------------------------------------------------------
# from docd to ...
#-------------------------------------------------------
def docd2lorl(docd, rowkeys, colkeys):
    # better way?
    return locl2lorl(docd2locl(docd, rowkeys, colkeys))

def docd2locl(docd, rowkeys, colkeys):
    return [[docd[ckey][rkey]
             for rkey in rowkeys if rkey in docd[ckey]]
            for ckey in colkeys if ckey in docd]

def docd2lord(docd, rowkeys, colkeys):
    # better way?
    return locd2lord(docd2locd(docd, colkeys), rowkeys, colkeys)

def docd2locd(docd, colkeys):
    return [docd[ckey] for ckey in colkeys]

def docd2dorl(docd, rowkeys, colkeys):
    # better way?
    return lorl2dorl(locl2lorl(docd2locl(docd, rowkeys, colkeys)),
                     rowkeys)

def docd2docl(docd, rowkeys, colkeys):
    # don't need zip
    return dict([(ckey, [docd[ckey][rkey]
                         for rkey in rowkeys if rkey in docd[ckey]])
                 for ckey in colkeys])

def docd2dord(docd, rowkeys, colkeys):
    # better way?
    return lorl2dord(locl2lorl(docd2locl(docd, rowkeys, colkeys)),
                     rowkeys, colkeys)

[Read the full post...]

About

This is my *OLD* blog. I've copied all of my posts and comments over to my NEW blog at:

http://www.saltycrane.com/blog/.

Please go there for my updated posts. I will leave this blog up for a short time, but eventually plan to delete it. Thanks for reading.