3

Here's the code in question:

  class Server(SocketServer.ForkingMixIn, SocketServer.TCPServer):
     __slots__ = ("loaded")

  class Handler(SocketServer.StreamRequestHandler):
     def handle(self):
        print self.server.loaded # Prints "False" at every call, why?
        self.server.loaded = True
        print self.server.loaded # Prints "True" at every call, obvious!

  server = Server(('localhost', port), Handler)
  server.loaded = False

  while True:
     server.handle_request()

Every time a new request comes in, the output I get is False followed by True. What I want is False followed by True the first time, and True followed by True henceforth.

Why aren't the modifications I made to the variable in the server instance persisting outside the scope of the handler's handle() function?

UPDATED:

So, I try using global variables to achieve what I want:

  loaded = False

  class Server(SocketServer.ForkingMixIn, SocketServer.TCPServer):
     pass

  class Handler(SocketServer.StreamRequestHandler):
     def handle(self):
        global loaded
        print loaded # Prints "False" at every call still, why?
        loaded = True
        print loaded # Prints "True" at every call, obvious!

  def main():
     server = Server(('localhost', 4444), Handler)
     global loaded
     loaded = False

     while True:
        server.handle_request()

  if (__name__ == '__main__'):
     main()

And it still doesn't work, i.e. produces the same output as before. Could anyone please tell me where I'm going wrong?

Noctis Skytower
  • 21,433
  • 16
  • 79
  • 117
  • From the Python docs: "If a name is declared global, then all references and assignments go directly to the middle scope containing the module’s global names. Otherwise, all variables found outside of the innermost scope are read-only (an attempt to write to such a variable will simply create a new local variable in the innermost scope, leaving the identically named outer variable unchanged)." So, I think, my problem comes down to getting around this. – Mikhil Masli Oct 05 '10 at 22:15
  • I think it might have to do with the ForkingMixIn class. Try removing the ForkingMixIn base class and see if that changes anything. – flashk Oct 05 '10 at 22:38
  • Umm, tried that. Didn't work. I think the problem is with sharing the same *writable* scope between two calls to the Handler instance. – Mikhil Masli Oct 05 '10 at 23:10

2 Answers2

4

Forking creates a new process, so you can't modify the server's variables in the original process. Try the ThreadingTCPServer instead:

import SocketServer

class Server(SocketServer.ThreadingTCPServer):
    __slots__ = ("loaded")

class Handler(SocketServer.StreamRequestHandler):
    def handle(self):
        self.server.loaded = not self.server.loaded
        print self.server.loaded # Alternates value at each new request now.

server = Server(('localhost',5000),Handler)
server.loaded = False

while True:
    server.handle_request()
Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • Oh I see. I think this should work. However, I haven't tried it. I just shared the variable between processes using a file and all is well. – Mikhil Masli Oct 11 '10 at 14:42
2

Your problem is that SocketServer.ForkingMixin creates a new process for every request. Therefore, every time a new request comes in all your variables get reset to their default state. So essentially no matter what you assign to self.server.loaded it will get reset at the next request. This is also why globals won't work.

If you need a variable to persist between requests you'd best write that data somewhere more, er, persistent =). Essentially, it sounds like you're trying to solve the problem of keeping session variables. There's a million and one ways to do it and depending on your situation, one way might be more pertinent than another. I highly recommend looking at how other Python-based SocketServer applications do it.

Just do a quick google for similar code to yours: "filetype:py SocketServer ForkingMixIn" (one of the first results is CherryPy which I highly recommend looking at).

Dan McDougall
  • 9,417
  • 3
  • 17
  • 17