Is crawling the stack "bad"? Why?

Russell Warren russandheather at gmail.com
Sun Feb 24 23:04:34 EST 2008


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()



More information about the Python-list mailing list