Reputation: 1286
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
Reputation: 23591
The issue here that BaseHttpRequestHandler (and thus its subclass, SimpleHttpRequestHandler) never makes it out of its __init__
method:
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
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
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