Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing extra metadata to a RequestHandler using python's SocketServer and Children

Tags:

python

sockets

I'm implementing a python application which is using ThreadingTCPServer and a custom subclass of BaseRequestHandler. The problem with this is that the ThreadingTCPServer seems to automatically spawn threads and create instances of the handler, calling their handle() function. However this leaves me with no way to pass data to the handler other than using global variables or class variables, both of which seem hackish. Is there any better way to do it?

Ideally this should be something like:

class ThreadedTCPServer(ThreadingTCPServer):
    def process_request(self, *args, **kwargs):
        ThreadingTCPServer.process_request(self, data, *args, **kwargs)

with the handler like

class ThreadedTCPRequestHandler(BaseRequestHandler):
    def handle(self,data):
        #do something with data
like image 523
Sudarshan S Avatar asked Sep 02 '12 06:09

Sudarshan S


2 Answers

I stumbled upon the very same thing. My solution was the following:

class ThreadedTCPRequestHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        print(self.server.mycustomdata)

class ThreadedTCPServer(SocketServer.ThreadingTCPServer):
    pass

server = ThreadedTCPServer((args.host, args.port), ThreadedTCPRequestHandler)
server.mycustomdata = 'foo.bar.z'
server.serve_forever()

The RequestHandler is called with a server object as a third parameter, and it is saved as self.server attribute, so you can access it. If you would set this attribute to a callable, you could easily call it, too:

def handle(self):
    mycustomdata = self.server.mycustomdata()
like image 56
toudi Avatar answered Sep 19 '22 16:09

toudi


The first answer worked for me, but I think it is cleaner to alter the __init__ method and pass the attribute in the constructor:

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):

    def __init__(self, host_port_tuple, streamhandler, Controllers):
        super().__init__(host_port_tuple, streamhandler)
        self.Controllers = Controllers

Note the third parameter 'Controllers' in the constructor, then the call to super without that parameter, then setting the new attribute Controllers to the property self.Controllers. The rest of the class is unchanged. Then, in your Requesthandler, you get access to the parameter using the 'server' attribute, as described above:

def handle(self):        
    self.Controllers = self.server.Controllers
    <rest of your code>

It's much the same as the answer above but I find it a little cleaner because the constructor is overloaded and you simply add the attribute you want in the constructor:

server = ServerInterface.ThreadedTCPServer((HOST, PORT), ServerInterface.ThreadedTCPRequestHandler, Controllers)
like image 45
Blindfreddy Avatar answered Sep 22 '22 16:09

Blindfreddy