With python socketserver how can I pass a variable to the constructor of the handler class
I would like to pass my datab开发者_StackOverflowase connection to the EchoHandler class, however I can't figure out how to do that or access the EchoHandler class at all.
class EchoHandler(SocketServer.StreamRequestHandler): def handle(self): print self.client_address, 'connected' if __name__ == '__main__': conn = MySQLdb.connect (host = "10.0.0.5", user = "user", passwd = "pass", db = "database") SocketServer.ForkingTCPServer.allow_reuse_address = 1 server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler) print "Server listening on localhost:4242..." try: server.allow_reuse_address server.serve_forever() except KeyboardInterrupt: print "\nbailing..."
Unfortunately, there really isn't an easy way to access the handlers directly from outside the server.
You have two options to get the information to the EchoHandler instances:
- Store the connection as a property of the server (add
server.conn = conn
before callingserver_forever()
) and then access that property in EchoHandler.handler throughself.server.conn
. - You can overwrite the server's
finish_request
and assign the value there (you would have to pass it to the constructor of EchoHandler and overwrite EchoHandler.__init__). That is a far messier solution and it pretty much requires you to store the connection on the server anyway.
My optionon of your best bet:
class EchoHandler(SocketServer.StreamRequestHandler):
def handle(self):
# I have no idea why you would print this but this is an example
print( self.server.conn );
print self.client_address, 'connected'
if __name__ == '__main__':
SocketServer.ForkingTCPServer.allow_reuse_address = 1
server = SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler)
server.conn = MySQLdb.connect (host = "10.0.0.5",
user = "user", passwd = "pass", db = "database")
# continue as normal
Mark T has the following to say on the python list archive
In the handler class, self.server refers to the server object, so subclass the server and override init to take any additional server parameters and store them as instance variables.
import SocketServer
class MyServer(SocketServer.ThreadingTCPServer):
def __init__(self, server_address, RequestHandlerClass, arg1, arg2):
SocketServer.ThreadingTCPServer.__init__(self,
server_address,
RequestHandlerClass)
self.arg1 = arg1
self.arg2 = arg2
class MyHandler(SocketServer.StreamRequestHandler):
def handle(self):
print self.server.arg1
print self.server.arg2
Another way, that I believe more pythonic, is to do the following:
class EchoHandler(SocketServer.StreamRequestHandler):
def __init__(self, a, b):
self.a = a
self.b = b
def __call__(self, request, client_address, server):
h = EchoHandler(self.a, self.b)
SocketServer.StreamRequestHandler.__init__(h, request, client_address, server)
You can now give an instance of your handler to the TCPServer:
SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler("aaa", "bbb"))
The TCPServer normally creates a new instance of EchoHandler per request but in this case, the __call__ method will be called instead of the constructor (it is already an instance.)
In the call method, I explicitly make a copy of the current EchoHandler and pass it to the super constructor to conform to the original logic of "one handler instance per request".
It is worth having a look at the SocketServer module to understand what happens here: https://github.com/python/cpython/blob/2.7/Lib/SocketServer.py
I was currently solving same problem, but I used slightly different solution, I feel it's slightly nicer and more general (inspired by @aramaki).
In the EchoHandler
you just need to overwrite __init__
and specify custom Creator
method.
class EchoHandler(SocketServer.StreamRequestHandler):
def __init__(self, request, client_address, server, a, b):
self.a = a
self.b = b
# super().__init__() must be called at the end
# because it's immediately calling handle method
super().__init__(request, client_address, server)
@classmethod
def Creator(cls, *args, **kwargs):
def _HandlerCreator(request, client_address, server):
cls(request, client_address, server, *args, **kwargs)
return _HandlerCreator
Then you can just call the Creator
method and pass anything you need.
SocketServer.ForkingTCPServer(('10.0.0.6', 4242), EchoHandler.Creator(0, "foo"))
Main benefit is, that this way you are not creating any more instances than necessary and you are extending the class in more manageable way - you don't need to change the Creator
method ever again.
It seems that you can't use ForkingServer
to share variables because Copy-on-Write happens when a process tries to modify a shared variable.
Change it to ThreadingServer
and you'll be able to share global variables.
精彩评论