Index: Lib/xmlrpclib.py =================================================================== --- Lib/xmlrpclib.py (revision 77393) +++ Lib/xmlrpclib.py (working copy) @@ -257,6 +257,28 @@ """Indicates a broken response package.""" pass + +class MarshallError(Error): + """Indicates an error occured while marshalling to/from XML""" + def __init__(self, message, value, key=None, slot=None): + self.msg = message + self.value = value + self.slot = slot + self.key = key + + def __repr__(self): + message = str(self.msg) + + if self.slot is not None: + message += "; slot %s" % self.slot + + if self.key is not None: + message += "; key %s" % self.key + + message += "; value='%s'" % self.value + + return " %s " % message + ## # Indicates an XML-RPC fault response package. This exception is # raised by the unmarshalling layer, if the XML-RPC response contains @@ -608,17 +630,24 @@ dispatch = {} + def dump_fault(self, fault, write, dump): + '''dumps the fault provided to the writer''' + self._dump_fault(fault.faultCode, fault.faultString, write, dump) + + def _dump_fault(self, faultCode, faultString, write, dump): + '''dumps the fault code and string to the writer provided''' + write("\n") + dump({'faultCode': faultCode, + 'faultString': faultString}, + write) + write("\n") + def dumps(self, values): out = [] write = out.append dump = self.__dump if isinstance(values, Fault): - # fault instance - write("\n") - dump({'faultCode': values.faultCode, - 'faultString': values.faultString}, - write) - write("\n") + self.dump_fault(values, write, dump) else: # parameter block # FIXME: the xml-rpc specification allows us to leave out @@ -626,12 +655,27 @@ # however, changing this may break older code (including # old versions of xmlrpclib.py), so this is better left as # is for now. See @XMLRPC3 for more information. /F - write("\n") - for v in values: - write("\n") - dump(v, write) - write("\n") - write("\n") + try: + params = [] + write = params.append + + write("\n") + # pos is the position of a failed serialization + for pos, v in enumerate(values): + write("\n") + dump(v, write) + write("\n") + write("\n") + + out.extend(params) + except MarshallError as e: + fault = [] + write = fault.append + message = "could not serialize value #%d: %s" % (pos, e) + self._dump_fault(8002, message, write, dump) + + out.extend(fault) + result = string.join(out, "") return result @@ -710,8 +754,12 @@ self.memo[i] = None dump = self.__dump write("\n") - for v in value: - dump(v, write) + for pos, v in enumerate(value): + try: + dump(v, write) + except TypeError as e: + raise MarshallError(e, v, slot=pos) + write("\n") del self.memo[i] dispatch[TupleType] = dump_array @@ -732,7 +780,11 @@ else: raise TypeError, "dictionary key must be string" write("%s\n" % escape(k)) - dump(v, write) + try: + dump(v, write) + except Exception as e: + raise MarshallError(e, v, key=k) + write("\n") write("\n") del self.memo[i]