getting full exception information from an xmlrpc server

S

Scott

I'm debugging an xmlrpc client/server application. Often when an
exception occurs in the server, I receive only a very short error
message on the client. For example:

xmlrpclib.Fault: <Fault 1: "<type 'exceptions.AssertionError'>:">

Presumably this is because xmlrpclib on the server is catching the
exception, and only sending the exception name to the client, not the
server's stack trace.

What I would like is the full stack trace of what went wrong on the
server (i.e. the junk python usually dumps to the console when an
exception occurs). I don't care which side I see the dump on (client
or server), but I need to see the whole trace so I can figure out what
the problem is. Is there an easy way to do this?

Thanks,
Scott
 
F

Fredrik Lundh

Scott said:
I'm debugging an xmlrpc client/server application. Often when an
exception occurs in the server, I receive only a very short error
message on the client. For example:

xmlrpclib.Fault: <Fault 1: "<type 'exceptions.AssertionError'>:">

Presumably this is because xmlrpclib on the server is catching the
exception, and only sending the exception name to the client, not the
server's stack trace.

xmlrpclib is a client library. The faultString message you see here is
provided by the server.

</F>
 
D

draghuram

What I would like is the full stack trace of what went wrong on the
server (i.e. the junk python usually dumps to the console when an
exception occurs). I don't care which side I see the dump on (client
or server), but I need to see the whole trace so I can figure out what
the problem is. Is there an easy way to do this?

I had the same need and I am copying the code I used here. Please note
that the code does a little bit more than getting the full stack
traces. To use it, simply copy it to a file and use "Server" and
"ServerProxy" from this module.

Thanks,
Raghu

---

'''

This package provides a userful wrapper around the xmlrpc client and
server
from stdlib. The main benefits include:

The server provides functions alive and kill. More over, any exception
generated
in rpc functions result in the whole stack trace to be sent across.
The client
processes this stack trace and re-raises the exception.

'''

from SimpleXMLRPCServer import SimpleXMLRPCServer
import xmlrpclib
import sys
import socket
import SocketServer
import traceback

# All public methods in this class are callable by clients.
class UtilityFuncs(object):
def __init__(self, *args, **kwargs):
self.running = True

def kill(self):
self.running = False
return True

def alive(self):
return True

class Server(SimpleXMLRPCServer):
def __init__(self, *args, **kwargs):
self.util_inst = UtilityFuncs()
SimpleXMLRPCServer.__init__(self, *args, **kwargs)
self.register_instance(self.util_inst)

def server_bind(self):
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,
1)
SimpleXMLRPCServer.server_bind(self)

# The default dispatcher does not send across the whole stack
trace.
# Only type and value are passed back. The client has no way of
knowing
# the exact place where error occurred in the server (short of
some
# other means such as server logging). This dispatcher sends the
whole
# stack trace.
def _dispatch(self, method, params):
try:
return SimpleXMLRPCServer._dispatch(self, method, params)
except:
# can't use format_exc() as it is not available in jython
yet (even
# in trunk).
type, value, tb = sys.exc_info()
raise xmlrpclib.Fault(1,
''.join(traceback.format_exception(type, value, tb)))

def serve_until_done(self):
# In a threaded server, kill() may need to be called twice.
The first,
# time it is called, the loop may test 'running' before the
new thread
# has a chance to set it to False.
while self.util_inst.running:
self.handle_request()

class ThreadedServer(SocketServer.ThreadingMixIn, Server):
pass

# A special exception has been defined just to indicate in the client
that the exception
# has in fact originated on the server.
class ServerException(Exception):
pass

# The server sends the whole stack trace as a string. Convert it back
to
# an exception here.
class ExceptionUnmarshaller(xmlrpclib.Unmarshaller):
def close(self):
try:
return xmlrpclib.Unmarshaller.close(self)
except xmlrpclib.Fault, e:
raise ServerException(e.faultString)

class ExceptionTransport(xmlrpclib.Transport):
# getparser() in xmlrpclib.Transport has logic to choose fastest
parser
# available. The parser needs to be passed an unmarshaller.
Unfortunately,
# getparser() there does not take unmarshaller as a parameter so
we
# can not simply call it with our unmarshaller. Either the whole
code
# there needs to be replicated here using our unmarshaller or we
use
# a much simpler version. The latter is chosen (partly because the
code is
# inspired by ASPN recipe 365244.
def getparser(self):
unmarshaller = ExceptionUnmarshaller()
parser = xmlrpclib.ExpatParser(unmarshaller)
return parser, unmarshaller

class ServerProxy(xmlrpclib.ServerProxy):
def __init__ (self, *args, **kwargs):
# Supply our own transport
try:
kwargs['transport']
except:
# This is expected
pass
else:
raise Exception('A transport (%s) is provided. This is not
expected as '
'a custom transport is being used' %
kwargs['transport'])

kwargs['transport'] = ExceptionTransport()
xmlrpclib.ServerProxy.__init__(self, *args, **kwargs)
---
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top