开发者

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:

  1. Store the connection as a property of the server (add server.conn = conn before calling server_forever()) and then access that property in EchoHandler.handler through self.server.conn.
  2. 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜