user1429322
user1429322

Reputation: 1286

Pass Parameter to SimpleHTTPRequestHandler

I have to pass a parameter to SimpleHTTPRequestHandler class, so I used class factory to create a custom handler as below.

def RequestHandlerClass(application_path):    

  class CustomHandler(SimpleHTTPRequestHandler):

    def __init__(self, request, client_address, server):

        SimpleHTTPRequestHandler.__init__(self, request, client_address, server)
        self._file_1_done = False 
        self._file_2_done = False 
        self._application_path = application_path

    def _reset_flags(self):

        self._file_1_done = False 
        self._file_2_done = False 

    def do_GET(self):

        if (self.path == '/file1.qml'):
            self._file_1_done = True  

        if (self.path == '/file2.qml'):
            self._file_2_done = True 

        filepath = self._application_path + '/' + self.path  # Error here

        try:
            f = open(filepath) 
            self.send_response(200)
            self.end_headers()
            self.wfile.write(f.read())
            f.close()
        except IOError as e :
            self.send_error(404,'File Not Found: %s' % self.path)    


        if (self._file_1_done and self._file_2_done):
            self._reset_flags()  
            self.server.app_download_complete_event.set()
  return CustomHandler 

This is my httpserver using the custom handler

class PythonHtpServer(BaseHTTPServer.HTTPServer, threading.Thread):

  def __init__(self, port, serve_path):
    custom_request_handler_class = RequestHandlerClass(serve_path)
    BaseHTTPServer.HTTPServer.__init__(self, ('0.0.0.0', port), custom_request_handler_class)
    threading.Thread.__init__(self)
    self.app_download_complete_event = threading.Event()

  def run(self):
    self.serve_forever()

  def stop(self):
    self.shutdown()    

and I start the server with

http_server = PythonHtpServer(port = 8123, serve_path = '/application/main.qml')

The server starts, but I get this error

AttributeError: CustomHandler instance has no attribute '_application_path'

Basically, from the error, the server did start but I don't know why it is not creating the attributes ( or the init is not called ). Please tell me where I am going wrong. Any help would be welcome.

Upvotes: 4

Views: 5244

Answers (3)

kris
kris

Reputation: 23591

The issue here that BaseHttpRequestHandler (and thus its subclass, SimpleHttpRequestHandler) never makes it out of its __init__ method:

  • HttpServer calls the SimpleHttpRequestHandler's constructor, which percolates up to the parent class' BaseHttpHandler__init__...
  • BaseHttpHandler.__init__ calls handle, which is overridden in...
  • BaseHttpRequestHandler.handle, which loops forever (given default options).

Thus, if you want to pass info along to your handler, you have to do it before calling the super's __init__. I'm probably several years too late to help you, but for future readers, you can just switch the order of your __init__:

class CustomHandler(http.server.SimpleHTTPRequestHandler):

  def __init__(self, *args, **kwargs):
    self._file_1_done = False 
    self._file_2_done = False
    self._application_path = application_path
    super().__init__(*args, **kwargs)

I'd probably also use functools.partial instead of wrapping the whole thing in a function:

class CustomHandler(http.server.SimpleHTTPRequestHandler):

  def __init__(self, application_path, *args, **kwargs):
    self._file_1_done = False 
    self._file_2_done = False
    self._application_path = application_path
    super().__init__(*args, **kwargs)

...

custom_request_handler_class = functools.partial(CustomHandler, serve_path)
BaseHTTPServer.HTTPServer(('0.0.0.0', port), custom_request_handler_class)

It's probably not a great idea to call super().__init__ at the end of your init in general (since it might mess up initialization), but here it seems like Python is kind of forcing your hand.

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 149155

IMHO the simplest way is to make _application_path a static attribute of the class. It is declared only at class declaration time, and can be used transparently by instances of the class:

def RequestHandlerClass(application_path):    

  class CustomHandler(SimpleHTTPRequestHandler):

    _application_path = application_path   # static attribute

    def __init__(self, request, client_address, server):

        SimpleHTTPRequestHandler.__init__(self, request, client_address, server)
        self._file_1_done = False 
        self._file_2_done = False 

    def _reset_flags(self):
        ...

That way every new instance of the custom handler class will have access to the application path as self._application_path.

Upvotes: 3

CrepeGoat
CrepeGoat

Reputation: 2515

Conceptually, you've written something like this (in this e.g., application_path ~= var):

def createClass(var):
    class MyClass:
        def __init__(self):
            self.var = var
        def func(self):
            # use var in some way
            print (self.var)
    # Return class definition
    return MyClass

So the class is written to save the variable var when an instance of MyClass is created. However, by the time the function finishes, the variable is destroyed, and since the class only returns a class definition and not a class instance, an instance of MyClass does not get created by the time the original variable var is destroyed, and thus the variable var never actually gets saved by MyClass.

INSTEAD, you can add var as an argument to the MyClass.__init__ function, and create a generator class to handle creation of MyClass instances, like so:

class MyClass:
    def __init__(self, arg1, arg2, var):
        (self.arg1, self.arg2, self.var) = (arg1, arg2, var)
    # Insert other methods as usual

class MyClassGenerator:
    def __init__(self, var):
        self.var = var
    def make(self, arg1, arg2):
        return MyClass(arg1, arg2, var)

Upvotes: 0

Related Questions