I've got a case where I would like to know exactly what IP address a client made an RPC request from. This info needs to be known inside the RPC function. I also want to make sure that the IP address obtained is definitely the correct one for the client being served by the immediate function call. That is kind of dumb/obvious to say, but I do just to highlight that it could be a problem for an RPC server allowing multiple simultaneous connections on multiple threads. ie: I can't set some simple "current_peer_info" variable when the connection is made and let the RPC function grab that value later since by the time it does it could easily be wrong.
In order to solve this I toyed with a few schemes, but have (so far) settled on crawling up the stack from within the RPC call to a point where the precise connection info that triggered the RPC call to run could be determined. This makes sure (I think!) that I get the exact connection info in the event of a lot of simultaneous executions on different threads. It seems hackish, though. I frequently find that I take the long way around to do something only to find out later that there is a nice and tight pythonic way to get it done. This seems like it might be one of those cases and the back of my mind keeps trying to relegate this into the realm of cheat code that will cause me major pain later. I can't stop thinking the old days and slapping gotos all over code to fix something "quickly" rather than restructuring properly. Crawling around the stack in non-debugger code always seems nasty to me, but it sure seems to work nicely in this case... To illustrate this scheme I've got a short program using SimpleXMLRPCServer to do it. The code is below. If you run it you should get an output something like: RPC call came in on: ('127.0.0.1', 42264) Does anyone have a better way of doing this? Anyone want to warn me off of crawling the stack to get this type of info? The docstring for sys._getframe already warns me off by saying "This function should be used for internal and specialized purposes only", but without providing any convincing argument why that is the case. I'd love to hear a reasonable argument... the only thing I can think of is that it starts dipping into lower level language behavior and might cause problems if your aren't careful. Which is almost as vague as "for internal and specialized purposes only". I'm very curious to hear what you python wizards have to say. ---- import SimpleXMLRPCServer, xmlrpclib, threading, sys def GetCallerNameAndArgs(StackDepth = 1): """This function returns a tuple (a,b) where: a = The name of the calling function b = A dictionary with the arg values in order """ f = sys._getframe(StackDepth + 1) #+1 to account for this call callerName = f.f_code.co_name #get the arg count for the frame... argCount = f.f_code.co_argcount #get a tuple with the local vars in the frame (puts the args first)... localVars = f.f_code.co_varnames #now get the tuple of just the args... argNames = localVars[:argCount] #now to make a dictionary of args and values... argDict = {} for key in argNames: argDict[key] = f.f_locals[key] return (callerName, argDict) def GetRpcClientConnectionInfo(): #Move up the stack to the right point to figure out client info... requestHandler = GetCallerNameAndArgs(4)[1]["self"] usedSocket = requestHandler.connection return str(usedSocket.getpeername()) def StartSession(): return "RPC call came in on: %s" % GetRpcClientConnectionInfo() class DaemonicServerLaunchThread(threading.Thread): def __init__(self, RpcServer, **kwargs): threading.Thread.__init__(self, **kwargs) self.setDaemon(1) self.server = RpcServer def run(self): self.server.serve_forever() rpcServer = SimpleXMLRPCServer.SimpleXMLRPCServer(("", 12390), \ logRequests = False) rpcServer.register_function(StartSession) slt = DaemonicServerLaunchThread(rpcServer) slt.start() sp = xmlrpclib.ServerProxy("http://localhost:12390") print sp.StartSession() -- http://mail.python.org/mailman/listinfo/python-list