pythonpython-2.7python-3.xbasehttpserver

How to get HTTPServer to pass my own class back with do_GET's?


I have a special database class instance that i want passed into an HTTPServer handler so that I can have it returned for use in my do_GET and do_POST callback handlers. I tried subclassing to add my database class as an additional argument to the Handler... However that does not get it all the way down into my HTTPServerRequestHandler class.

What I also tried that did not work:

Even if I do manage to get the argument added to the HTTPServerRequestHandler class, this only creates an error because the serve_forever will still callback the class using the original 4 (self+3) arguments, omitting my 5th (database) argument.

Previously, I would instantiate the database class as a Global constant, but that seems like a bad idea.

Here is what I have working so far:

This code successfully serves my page, but I have no access to any of the database methods (including my log handler, which is a part of the database instance):

def run_server(state_database, port):
    state_database.log.info('starting server.')
    server_address = ('', port)

    HandlerClass = MakeHandlerClassforDB(state_database)

    httpserver = HTTPServer(server_address, HandlerClass)
    state_database.log.info('Server loaded.')
    httpserver.serve_forever()


def MakeHandlerClassforDB(state_database):
    class CustomHandler(HTTPServerRequestHandler, object):
        def __init__(self, *args, **kwargs):
            self.database = state_database
            super(CustomHandler, self).__init__(*args, **kwargs)
    return CustomHandler


# HTTPRequestHandler class
# noinspection PyPep8Naming
class HTTPServerRequestHandler(BaseHTTPRequestHandler):
    def __init__(self, request, client_address, server):
        BaseHTTPRequestHandler.__init__(self, request, client_address, server)
        # would like to be able to get database into here as a self....
    def do_GET(self):
        ip = self.client_address[0].split(".")
        if ip[0] in self.page.blocked_ip_highs:
            # `self.database` does not resolve, so this does not work...
            self.database.log.info(
                "Ignored request from %s" % self.client_address)
            return
          ...

    def do_POST(self):
        time_start = time.time()
        # again, self.database does not exist in the callback
        self.database.log.debug(
            "Headers_________\n%s\n______________\n" % self.headers)

I also want this to work on both Python 3 and Python 2 (which is why I don't use super().__init__(...).)


Solution

  • This appears to work:

    def run_server(state_database, port):
        server_address = ('', port)
    
        handlerclass = makehandlerclassfordb(state_database)
        httpserver = HTTPServer(server_address, handlerclass)
    
        httpserver.serve_forever()
    
    
    def makehandlerclassfordb(state_database):
        class CustomHandler(HTTPServerRequestHandler, object):
            def __init__(self, *args, **kwargs):
                self.database = state_database
                self.page = HTMLPage()
    
                super(CustomHandler, self).__init__(*args, **kwargs)
        return CustomHandler
    
    
    class HTTPServerRequestHandler(BaseHTTPRequestHandler):
    
         def do_GET(self):
            self.database.log.debug(
                "Get Header_________%s______________" % self.headers)
    
            """both self.page and self.database DO resolve (although
                PyCharm does not think they should :P )"""
    
            ip = self.client_address[0].split(".")
            if ip[0] in self.page.blocked_ip_highs:
                self.database.log.info(
                    "Ignored request from %s" % self.client_address)
                return
    

    I could re-add the items to the class HTTPServerRequestHandler __init__, but this does not change the functionality and I see no reason to just to make an IDE error go away.