0

In the below script a simple webserver is created and there is a class that should sends GET data to a sqlite3 database.

import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()


class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super(DBLoggingHandler, self).__init__(*args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

DBLoggingHandler()

The webserver portion of the script works properly. I am seeing GET data in my terminal output. However, nothing is being written to the crazysean table that I created in the db. Do you see anything wrong with my script or is it likely something else?


Code has been changed to reflect changes that I have made, but it is still not writing to the db.

UPDATED CODE:

import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()
4
  • You're never doing a commit() on the connection. Commented Aug 14, 2014 at 21:15
  • First things first: (1) Did you CREATE TABLE crazysean somewhere in that database? (2) Is Are you trying to keep the same database open in this program and also in an editor (the sqlite3 command-line, a GUI editor, whatever) at the same time? Commented Aug 14, 2014 at 21:28
  • Yes, I did create the table already. Not sure what you mean in question 2, but I send a few requests to the server and then open up the sqlite CLI and try SELECT * FROM crazysean and still nothing. I guess the db is always open, correct? I never close it in the script. Would this cause issues? Commented Aug 14, 2014 at 21:30
  • 1
    You've found yourself a real time capsule of state-of-the-1990s-art Python to work on here. :) A few minor bug fixes, and that's all the changes as far back as the revision history goes (2001). It's always extra fun to work with code like this… Commented Aug 14, 2014 at 22:38

3 Answers 3

2

First you do this:

httpd.serve_forever()

And then you create the handler class and instantiate it.

So, your code doesn't do anything until after "forever". Which is way too long to wait for it.

What you want to do is pass your handler subclass to the server, in place of the base class. Where you have this:

Handler = SimpleHTTPServer.SimpleHTTPRequestHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

… instead do this:

Handler = DBLoggingHandler
httpd = SocketServer.TCPServer(("", PORT), Handler)

(Or just get rid of the extra line and do httpd = SocketServer.TCPServer(("", PORT), DBLoggingHandler).)

You will also have to move the definition of your class up to the top of the file.


But there's another problem.

Python used to have two different kinds of classes: new-style and classic. They fixed that years ago in 3.0, but you're still using 2.something.

Normally you can just always use the new-style classes and never worry about the old kind, but every once in a while you run into a class in the stdlib or a third-party library that's still made the old way, and you want to inherit from it. And that's exactly what happened here. So now, sadly, you have to learn about old-style classes.

Among the things old-style classes can't do is super. The fix for this is to explicitly call the unbound method of the base class. So, instead of this:

super(DBLoggingHandler, self).__init__(*args, **kwargs)

… you need this:

SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(self, *args, **kwargs)

And likewise for the other super call.

The way you know this is happening (other than by looking at the base class) is when you get an error message about classobj or instance. Those two types always mean a classic class is involved somewhere.


But wait, there's more.

Whatever idiot told you to put that self.db = … stuff in the __init__ method was an idiot. :)

SocketServer handlers are a weird kind of class. Each new request constructs a new instance, and the __init__ method calls handle, which doesn't return until it's time to destroy the whole instance. So, anything you put in the __init__ after calling the base class won't happen until it's too late.

The simplest thing to do here is probably to make db a global variable. All else being equal, globals are bad. But in this case, the alternatives seem to be either overriding functionality from three different classes, or monkeypatching it into the server instance and relying on undocumented details to reach it.


Also, you probably want to close the database somewhere. Killing the program without closing the database should never leave it corrupted, but it can mean you lose the last transaction if you time it just wrong.

Since serve_forever never returns except by you hitting ctrl-c, you can't just put it after that line; you need a with statement (or a finally, or an except BaseException, or an atexit). I don't think sqlite3.Connection objects became context managers until a later version of Python, so you'll have to use the closing context manager.


So:

import contextlib
import SimpleHTTPServer
import SocketServer
import sqlite3

PORT = 8000

db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_GET(self):
        db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        db.commit()
        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self)

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
with contextlib.closing(db):
    httpd.serve_forever()
Sign up to request clarification or add additional context in comments.

8 Comments

Now I get this error File "server.py", line 9, in __init__ super(DBLoggingHandler, self).__init__(*args, **kwargs) TypeError: must be type, not classobj
@Meepl: Give me a sec, I'll explain that.
Thanks for being so helpful. This is all pretty foreign to me, but I'm trying to learn.
An interesting problem I never would have figured out without your assistance! So a new error has occured: File "server.py", line 12, in do_GET self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)", AttributeError: DBLoggingHandler instance has no attribute 'db' I am curious why this isn't happening on line 10 where db is first called...
@Meepl: Honestly, all of this would be easier if you used a modern web framework. Something like Flask may look like it has more of a learning curve than SimpleHTTPServer, but the fact that it's being regularly updated, used by thousands of people for real business, etc., and wasn't designed in the 90s, means that you don't run into stuff like this; almost everything you have to learn will be useful knowledge instead of archaic trivia.
|
2

You're missing the final magic words db.commit() :)

def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return super(DBLoggingHandler, self).do_GET()

You have another error and this one is bigger. You're not actually calling your code, just calling SimpleHTTPRequestHandler.

Full example:

class DBLoggingHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        SimpleHTTPServer.SimpleHTTPRequestHandler.__init__(*args, **kwargs)
        self.db = sqlite3.connect('/home/cron1admin/crazysean/crazysean.db')
    def do_GET(self):
        self.db.execute("INSERT INTO crazysean (command, vers, path) VALUES (?, ?, ?)",
                        (self.command, self.request_version, self.path))
        self.db.commit()
        return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET()

Handler = DBLoggingHandler

httpd = SocketServer.TCPServer(("", PORT), Handler)

print "serving at port", PORT
httpd.serve_forever()

5 Comments

So, obviously this is needed and I added it to my script, but it is still not working
Yes, it is still not writing anything. I also added import sqlite3 which I did not have before and still not writing anything.
Did you just put the class above all of the webserver stuff? Curious how this changes things. Testing now.
Not just that, see? I substituted Handler assignment for your actual class implementation.
Now I get this error File "server.py", line 9, in __init__ super(DBLoggingHandler, self).__init__(*args, **kwargs) TypeError: must be type, not classobj
0

Take a look at the example at the top of:

https://docs.python.org/2/library/sqlite3.html

You need to create a cursor, execute statements through the cursor, then commit the connection.

1 Comment

You're right, but actually connection.cursor() is a shortcut which creates a temporal cursor object so is rather convenient to use connection.execute(): docs.python.org/2/library/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.