Archives

February 2010 (1)
September 2009 (1)
May 2009 (1)
April 2009 (1)
March 2009 (4)
January 2009 (3)

November 2008 (2)
October 2008 (2)
September 2008 (1)
August 2008 (5)
July 2008 (3)
June 2008 (1)
May 2008 (5)
April 2008 (8)
March 2008 (3)
February 2008 (1)
January 2008 (2)

December 2007 (2)
November 2007 (4)
October 2007 (17)
September 2007 (9)

My work here is done

Monday, October 22 2007

A few weeks ago I set out to write myself a blogging engine, which I have dubbed “Burble”. The motivation was partly to get off Blosxom, which I’d been finding pretty limiting, but mostly so I could come to grips with developing Python web applications.

And now I’m done. It was surprisingly easy. I’m even running on my own tiny frameworklet, which I have called Whisky, after WSGI. I’m going to put the code online soon, not because it’s ready for other people, but as an example (or a warning).

It’s pretty damned zippy too, even with all my old content from the other site. Network latency is by far the biggest factor in Burble’s performance. I’ve done my best to tweak that by turning on compression in Apache and minimising the markup. I have found YSlow invaluable for this.

(It’s worth a post on its own, but by judicious use of mod_deflate and mod_expires, guided by YSlow, I managed to get one of our indispensible extranet apps at work loading in less than 5 seconds, down from about 20, rendering it far, far more usable.)

 Now I have to find something else to do. I think the Ruminator is next. I have plans to use some stemming software and a bit of Bayesian analysis and a better visual representation that indicates links between stories.

1 comment

Tags: burble ~ web development ~ yslow ~ apache

Usability, or, Physician, heal thyself

Tuesday, October 16 2007

Burble has a nifty comment spam preventer, the details of which need not detain us here. But anyway, the spam-resistant comment process does require an extra step. This is partly to fool robots which mindlessly submit and then go away, and partly so I can do some magic.

I had Iabelled the buttons for the three steps Preview → Save → Confirm. That matched my mental model and the labels I had given these steps in my head.

A little log file inspection revealed that a couple of people had abandoned comments at the Save step. And I realised that by using the word “save” I had given the impression that the user’s comment was already persisted at the second step (it isn’t).

I have fixed the button labels to reflect what’s really happening. They are now Preview → Confirm → Save. And lo, now I have comments.

no comments

Tags: burble ~ usability ~ web development

A tiny WSGI framework in an hour or two

Sunday, October 14 2007

 I’m not sure whether the first story here is meant to encourage or dissuade, but I am writing my own WSGI framework to support Burble. Colubrid is holding me back, and its replacement Werkzeug is overkill for what I want. (To be fair, Colubrid has been a great help to me in getting started.) I’m really getting into the educational aspect of doing things from scratch where I can.

It turns out that putting together a very lightweight WSGI framework is very easy indeed, especially having made a small compromise by using a few pre-built things from Ian Bicking’s Paste. (Yeah, I contradict myself. I am large, I contain multitudes.)

It’s so easy that I’m almost done, so I present a tiny, noddy framework for your reading pleasure. It implements a regex-based URL dispatcher a la Web.py.


#!/usr/bin/python
from paste.request import parse_formvars
from paste.response import HeaderDict
import re

def attrsfromdict(d):
"""From Python cookbook s6.18 p 280"""
self = d.pop('self')
for n,v in d.iteritems():
setattr(self, n, v)

def simplerepr(obj):
d = obj.__dict__
members = ', '.join([n + '=' + v.__repr__() for n,v in d.iteritems()])
return '%s(%s)' % (obj.__class__.__name__, members)

class NoMatchingControllerException(Exception):
pass

class Request(object):
def __init__(self, environ):
self.environ = environ
self.fields = parse_formvars(environ)

class Response(object):
def __init__(self,
status_code='200',
response_phrase="OK", body="",
headers=HeaderDict({'content-type': 'text/html'})
):
attrsfromdict(locals())
def __str__(self):
return simplerepr(self)
def __repr__(self):
return self.__str__()
def status(self):
return ' '.join([self.status_code, self.response_phrase])

class Dispatcher(object):
"""
The Dispatcher maintains an internal list of regexes and controllers.
The Dispatcher accepts strings, and tries to match in turn against
the regexes. As soon as a match is found, the corresponding controller is
invoked.
"""
def __init__(self, regex_app_tuples):
self.dispatch_list = []
for k, v in regex_app_tuples:
p = re.compile(k)
self.dispatch_list.append((p, v))

def dispatch(self, request):
"""
Expects to call a Controller's instance method GET or POST
with the request and the groups obtained from the regex
as arguments.
"""
path_info = request.environ.get('PATH_INFO', '')
method = request.environ['REQUEST_METHOD']

for pat, app in self.dispatch_list:
mo = pat.match(path_info)
if mo != None:
args = [request]
                args.extend([i for i in mo.groups()])
if method == 'GET':
return app.GET(*args)
elif method =='POST':
return app.POST(*args)
raise NoMatchingControllerException, "No match for %s" % path_info

class WhiskyApp(object):
def __init__(self, dispatcher):
self.dispatcher = dispatcher
def __call__(self, environ, start_response):
request = Request(environ)
response = self.dispatcher.dispatch(request)
start_response(response.status(), response.headers.items())
return [response.body]

class NoddyController(object):
def GET(self, request, id):
r = Response(body="Noddy got %s" % id)
return r

class BigEarsController(object):
def __init__(self):
print "in init"
def GET(self, request, arg1, arg2):
r = Response(body="Big Ears got %s and %s" % (arg1, arg2))
return r

if __name__ == '__main__':
from paste import httpserver
dispatch_list = [
(r'/noddy/(\d+)/?$', NoddyController()),
(r'/bigears/(.*?)/(\d+)/?$', BigEarsController())
]
dispatcher = Dispatcher(dispatch_list)
app = WhiskyApp(dispatcher)
httpserver.serve(app, host='127.0.0.1', port='8080')

That’s pretty much all I need, to be honest. I’m happy using Beaker for sessions, and I’ll probably pull in cookie stuff from Paste. I bodged up an Etag cache manager for Burble, which I want to integrate. I want to write a nice base class for controllers. And that’s it. Whee!

no comments

Tags: python ~ burble ~ wsgi ~ paste ~ programming ~ web development

Things I love about Firefox : view selection

Tuesday, October 09 2007

If you select a portion of a page and right-click, “View Selection” shows you the HTML for just that portion (or in some builds, the whole source, but with HTML corresponding to your selection highlighted). Invaluable to the developer, but not universally known.

no comments

Tags: firefox ~ web development

Recent comments

Rendered at 2010-08-01 22:17:26