I’ve been wanting to try web.py for a while. It seems like it’s easier to learn than Django for making python web applications. I made the hello world app quickly and simply, and decided I’d use it for a project I’m working on.
That’s when it turned south. I have to use php for another project I’m doing, and I use cgi-irc (in perl through CGI) on that server, so I didn’t want to re-setup all that stuff on lighttpd (the recommended method for running web.py). I tried running it through apache with cgi. After a few hours of that not working I switched to trying fastcgi on apache, also to no avail. I got fastcgi to the point where I was having an error that was common enough to be in an FAQ: I was getting 500 responses from apache because fastcgi/web.py wasn’t starting fast enough for the flup WSGI library to realize it, so it would start them over and over until it eventually gave up. I decided I just wanted to see the thing work at this point, so I installed lighttpd and went through the setup for that. When that method didn’t work I was fed up with their install instructions. I decided to try a method that John Quigley recommended. He said I should just run it with the internal http server that I’ve already used successfully and proxy the requests through apache to web.py. This might not achieve the goal of making the server large scale production ready, but I don’t really have to worry about that so much at this point. It does achieve my more important goal of making my php, cgi, and web.py applications all available through a single url and port, so I gave it a shot.
I looked up apache proxying and found it was done through mod_proxy, and each protocol you want to proxy has it’s own module as well. So I installed mod_proxy and mod_proxy_http and added the following line to my apache2.conf file:
ProxyPass /lifelog http://192.168.1.20:8888
192.168.1.20 being the IP address of the server in question. It would be better to get it resolving localhost so this line can be used on other servers, but I didn’t want to bother with it yet. I also had to change the proxy permission rules in proxy.conf to this:
Allow from all
This allows any user online to use my ProxyPass rule. Note that I left “ProxyRequests” set to “Off”. If I turned that on, I would be an open proxy that spammers and hackers could use to hide their identity during their nefarious behavior.
Now all that’s left is to start up my web.py server on port 8888 as specified in apache2.conf and whenever I go to /lifelog/* through apache, it’ll send the request to web.py.
Ubuntu added a nifty feature to bash recently: when you run a command that isn’t installed on your system it tells you what package you need to get it. What they need to do next is apply the same type of thing to apt. When I run “apt-get install flup” and it tells me there’s no package called “flup”, it should automatically run “apt-cache search flup” and recommend “Here are some packages you might have meant: … python-flup, …” Maybe it could even list the top 10 most likely packages and have me press 0-9 to install one of them. Either way, it’s silly that I have to first search for the package so I can find out what the distro named it, then run almost the same command again to install it.
I just spent some time figuring out how to handle exceptions using the pysqlite library. I couldn’t find a good description of how it works online, so I’m making one.
In python, all exceptions are classes. This means they can use any of the capabilities that regular python classes have, but generally all they do is define a few instance values in the __init__ method. These values are available in the exception object (the instance of the exception class) when it is caught in a “try … except” block.
If you’re receiving a user defined exception from a foreign module and don’t know how to handle it, I’d recommend first wrapping the offending code in a “try…except” like this:
logfile.write("ERROR: %s %s %s" % (sys.exc_info(), sys.exc_info(), sys.exc_info()))
The function sys.exc_info() returns a tuple containing 1) the type of exception being handled, 2) the associated parameter (usually some helpful descriptor), and 3) a traceback to where the exception occurred.
This hopefully tells you two things: a description of why this happened, and more importantly for my situation, the exception type. When I ran that I got something like this:
ERROR: <class 'pysqlite2.dbapi2.OperationalError'> table something already exists <traceback object at 0xb7da3cd4>
So the exception being raised was at pysqlite2.dbapi2.OperationalError. Since the python dbapi returns sql errors through exceptions I can’t just fix the code here, I have to “try … except” it. Here’s what the final code snippet looked like:
except pysqlite2.dbapi2.OperationalError, msg:
logfile.write("ERROR: %s" % msg)
To catch the other exceptions specified in the dbapi v2.0, you’d have to include several more exception handlers. The other exception classes are in the same location as the OperationalError class.
I’m restarting this blog with a new focus. Before I would post amusing happenings and general bloggy type things, but now I’ve moved that to my livejournal (a much better place for that type of blog) and this will be exclusively for talking about ideas and technologies about anything, from software to ancient Greek siege weapons. I’m mostly writing these things down for my benefit, but maybe the information will be useful to someone else too.