merged with SLAB codebase (version 1.0.1)
diff --git a/Lib/xmlrpclib.py b/Lib/xmlrpclib.py
index 6460aa2..0eb9f3f 100644
--- a/Lib/xmlrpclib.py
+++ b/Lib/xmlrpclib.py
@@ -33,12 +33,19 @@
 # 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
 # 2001-10-01 fl  Remove containers from memo cache when done with them
 # 2001-10-01 fl  Use faster escape method (80% dumps speedup)
+# 2001-10-02 fl  More dumps microtuning
+# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
 # 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
-# 2001-10-17 sm  test for int and long overflow (allows use on 64-bit systems)
+# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
 # 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
+# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
+# 2002-04-07 fl  Added pythondoc comments
+# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
+# 2002-05-15 fl  Added error constants (from Andrew Kuchling)
+# 2002-06-27 fl  Merged with Python CVS version
 #
-# Copyright (c) 1999-2001 by Secret Labs AB.
-# Copyright (c) 1999-2001 by Fredrik Lundh.
+# Copyright (c) 1999-2002 by Secret Labs AB.
+# Copyright (c) 1999-2002 by Fredrik Lundh.
 #
 # info@pythonware.com
 # http://www.pythonware.com
@@ -46,8 +53,8 @@
 # --------------------------------------------------------------------
 # The XML-RPC client interface is
 #
-# Copyright (c) 1999-2001 by Secret Labs AB
-# Copyright (c) 1999-2001 by Fredrik Lundh
+# Copyright (c) 1999-2002 by Secret Labs AB
+# Copyright (c) 1999-2002 by Fredrik Lundh
 #
 # By obtaining, using, and/or copying this software and/or its
 # associated documentation, you agree that you have read, understood,
@@ -73,13 +80,9 @@
 # --------------------------------------------------------------------
 
 #
-# things to look into:
+# things to look into some day:
 
-# TODO: support basic authentication (see robin's patch)
-# TODO: fix host tuple handling in the server constructor
-# TODO: let transport verify schemes
-# TODO: update documentation
-# TODO: authentication plugins
+# TODO: sort out True/False/boolean issues for Python 2.3
 
 """
 An XML-RPC client interface for Python.
@@ -130,6 +133,9 @@
 
 from types import *
 
+# --------------------------------------------------------------------
+# Internal stuff
+
 try:
     unicode
 except NameError:
@@ -146,9 +152,6 @@
     s = replace(s, "<", "&lt;")
     return replace(s, ">", "&gt;",)
 
-MAXINT =  2L**31-1
-MININT = -2L**31
-
 if unicode:
     def _stringify(string):
         # convert to 7-bit ascii if possible
@@ -160,16 +163,53 @@
     def _stringify(string):
         return string
 
-__version__ = "1.0.0"
+__version__ = "1.0.1"
+
+# xmlrpc integer limits
+MAXINT =  2L**31-1
+MININT = -2L**31
+
+# --------------------------------------------------------------------
+# Error constants (from Dan Libby's specification at
+# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
+
+# Ranges of errors
+PARSE_ERROR       = -32700
+SERVER_ERROR      = -32600
+APPLICATION_ERROR = -32500
+SYSTEM_ERROR      = -32400
+TRANSPORT_ERROR   = -32300
+
+# Specific errors
+NOT_WELLFORMED_ERROR  = -32700
+UNSUPPORTED_ENCODING  = -32701
+INVALID_ENCODING_CHAR = -32702
+INVALID_XMLRPC        = -32600
+METHOD_NOT_FOUND      = -32601
+INVALID_METHOD_PARAMS = -32602
+INTERNAL_ERROR        = -32603
 
 # --------------------------------------------------------------------
 # Exceptions
 
+##
+# Base class for all kinds of client-side errors.
+
 class Error(Exception):
     """Base class for client errors."""
     def __str__(self):
         return repr(self)
 
+##
+# Indicates an HTTP-level protocol error.  This is raised by the HTTP
+# transport layer, if the server returns an error code other than 200
+# (OK).
+#
+# @param url The target URL.
+# @param errcode The HTTP error code.
+# @param errmsg The HTTP error message.
+# @param headers The HTTP header dictionary.
+
 class ProtocolError(Error):
     """Indicates an HTTP protocol error."""
     def __init__(self, url, errcode, errmsg, headers):
@@ -184,10 +224,24 @@
             (self.url, self.errcode, self.errmsg)
             )
 
+##
+# Indicates a broken XML-RPC response package.  This exception is
+# raised by the unmarshalling layer, if the XML-RPC response is
+# malformed.
+
 class ResponseError(Error):
     """Indicates a broken response package."""
     pass
 
+##
+# Indicates an XML-RPC fault response package.  This exception is
+# raised by the unmarshalling layer, if the XML-RPC response contains
+# a fault string.  This exception can also used as a class, to
+# generate a fault XML-RPC message.
+#
+# @param faultCode The XML-RPC fault code.
+# @param faultString The XML-RPC fault string.
+
 class Fault(Error):
     """Indicates an XML-RPC fault package."""
     def __init__(self, faultCode, faultString, **extra):
@@ -203,6 +257,14 @@
 # --------------------------------------------------------------------
 # Special values
 
+##
+# Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
+# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
+# generate boolean XML-RPC values.
+#
+# @param value A boolean value.  Any true value is interpreted as True,
+#              all other values are interpreted as False.
+
 class Boolean:
     """Boolean-value wrapper.
 
@@ -234,9 +296,33 @@
 
 True, False = Boolean(1), Boolean(0)
 
-def boolean(value, truefalse=(False, True)):
+##
+# Map true or false value to XML-RPC boolean values.
+#
+# @def boolean(value)
+# @param value A boolean value.  Any true value is mapped to True,
+#              all other values are mapped to False.
+# @return xmlrpclib.True or xmlrpclib.False.
+# @see Boolean
+# @see True
+# @see False
+
+def boolean(value, _truefalse=(False, True)):
     """Convert any Python value to XML-RPC 'boolean'."""
-    return truefalse[operator.truth(value)]
+    return _truefalse[operator.truth(value)]
+
+##
+# Wrapper for XML-RPC DateTime values.  This converts a time value to
+# the format used by XML-RPC.
+# <p>
+# The value can be given as a string in the format
+# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
+# time.localtime()), or an integer value (as returned by time.time()).
+# The wrapper uses time.localtime() to convert an integer to a time
+# tuple.
+#
+# @param value The time, given as an ISO 8601 string, a time
+#              tuple, or a integer time value.
 
 class DateTime:
     """DateTime wrapper for an ISO 8601 string or time tuple or
@@ -258,8 +344,16 @@
             other = other.value
         return cmp(self.value, other)
 
+    ##
+    # Get date/time value.
+    #
+    # @return Date/time value, as an ISO 8601 string.
+
+    def __str__(self):
+        return self.value
+
     def __repr__(self):
-        return "<DateTime %s at %x>" % (self.value, id(self))
+        return "<DateTime %s at %x>" % (repr(self.value), id(self))
 
     def decode(self, data):
         self.value = string.strip(data)
@@ -269,17 +363,32 @@
         out.write(self.value)
         out.write("</dateTime.iso8601></value>\n")
 
-def datetime(data):
+def _datetime(data):
+    # decode xml element contents into a DateTime structure.
     value = DateTime()
     value.decode(data)
     return value
 
+##
+# Wrapper for binary data.  This can be used to transport any kind
+# of binary data over XML-RPC, using BASE64 encoding.
+#
+# @param data An 8-bit string containing arbitrary data.
+
 class Binary:
     """Wrapper for binary data."""
 
     def __init__(self, data=None):
         self.data = data
 
+    ##
+    # Get buffer contents.
+    #
+    # @return Buffer contents, as an 8-bit string.
+
+    def __str__(self):
+        return self.data or ""
+
     def __cmp__(self, other):
         if isinstance(other, Binary):
             other = other.data
@@ -295,7 +404,8 @@
         base64.encode(StringIO.StringIO(self.data), out)
         out.write("</base64></value>\n")
 
-def binary(data):
+def _binary(data):
+    # decode xml element contents into a Binary structure
     value = Binary()
     value.decode(data)
     return value
@@ -314,6 +424,12 @@
 except (AttributeError, ImportError):
     FastParser = FastUnmarshaller = None
 
+try:
+    import _xmlrpclib
+    FastMarshaller = _xmlrpclib.Marshaller
+except (AttributeError, ImportError):
+    FastMarshaller = None
+
 #
 # the SGMLOP parser is about 15x faster than Python's builtin
 # XML parser.  SGMLOP sources can be downloaded from:
@@ -367,13 +483,13 @@
 try:
     from xml.parsers import expat
     if not hasattr(expat, "ParserCreate"):
-        raise ImportError, "ParserCreate"
+        raise ImportError
 except ImportError:
-    ExpatParser = None
+    ExpatParser = None # expat not available
 else:
     class ExpatParser:
-        # fast expat parser for Python 2.0.  this is about 50%
-        # slower than sgmlop, on roundtrip testing
+        # fast expat parser for Python 2.0 and later.  this is about
+        # 50% slower than sgmlop, on roundtrip testing
         def __init__(self, target):
             self._parser = parser = expat.ParserCreate(None, None)
             self._target = target
@@ -403,6 +519,7 @@
         self.handle_xml = target.xml
         self.unknown_starttag = target.start
         self.handle_data = target.data
+        self.handle_cdata = target.data
         self.unknown_endtag = target.end
         try:
             xmllib.XMLParser.__init__(self, accept_utf8=1)
@@ -412,6 +529,13 @@
 # --------------------------------------------------------------------
 # XML-RPC marshalling and unmarshalling code
 
+##
+# XML-RPC marshaller.
+#
+# @param encoding Default encoding for 8-bit strings.  The default
+#     value is None (interpreted as UTF-8).
+# @see dumps
+
 class Marshaller:
     """Generate an XML-RPC params chunk from a Python data structure.
 
@@ -433,12 +557,13 @@
     dispatch = {}
 
     def dumps(self, values):
-        self.__out = []
-        self.write = write = self.__out.append
+        out = []
+        write = out.append
+        dump = self.__dump
         if isinstance(values, Fault):
             # fault instance
             write("<fault>\n")
-            self.__dump(vars(values))
+            dump(vars(values), write)
             write("</fault>\n")
         else:
             # parameter block
@@ -450,97 +575,105 @@
             write("<params>\n")
             for v in values:
                 write("<param>\n")
-                self.__dump(v)
+                dump(v, write)
                 write("</param>\n")
             write("</params>\n")
-        result = string.join(self.__out, "")
-        del self.__out, self.write # don't need this any more
+        result = string.join(out, "")
         return result
 
-    def __dump(self, value):
+    def __dump(self, value, write):
         try:
             f = self.dispatch[type(value)]
         except KeyError:
             raise TypeError, "cannot marshal %s objects" % type(value)
         else:
-            f(self, value)
+            f(self, value, write)
 
-    def dump_int(self, value):
+    def dump_int(self, value, write):
         # in case ints are > 32 bits
         if value > MAXINT or value < MININT:
             raise OverflowError, "int exceeds XML-RPC limits"
-        self.write("<value><int>%s</int></value>\n" % value)
+        write("<value><int>")
+        write(str(value))
+        write("</int></value>\n")
     dispatch[IntType] = dump_int
 
-    def dump_long(self, value):
-        # in case ints are > 32 bits
+    def dump_long(self, value, write):
         if value > MAXINT or value < MININT:
             raise OverflowError, "long int exceeds XML-RPC limits"
-        self.write("<value><int>%s</int></value>\n" % int(value))
+        write("<value><int>")
+        write(str(int(value)))
+        write("</int></value>\n")
     dispatch[LongType] = dump_long
 
-    def dump_double(self, value):
-        self.write("<value><double>%s</double></value>\n" % repr(value))
+    def dump_double(self, value, write):
+        write("<value><double>")
+        write(repr(value))
+        write("</double></value>\n")
     dispatch[FloatType] = dump_double
 
-    def dump_string(self, value, escape=escape):
-        self.write("<value><string>%s</string></value>\n" % escape(value))
+    def dump_string(self, value, write, escape=escape):
+        write("<value><string>")
+        write(escape(value))
+        write("</string></value>\n")
     dispatch[StringType] = dump_string
 
     if unicode:
-        def dump_unicode(self, value, escape=escape):
+        def dump_unicode(self, value, write, escape=escape):
             value = value.encode(self.encoding)
-            self.write("<value><string>%s</string></value>\n" % escape(value))
+            write("<value><string>")
+            write(escape(value))
+            write("</string></value>\n")
         dispatch[UnicodeType] = dump_unicode
 
-    def opencontainer(self, value):
-        if value:
-            i = id(value)
-            if i in self.memo:
-                raise TypeError, "cannot marshal recursive data structures"
-            self.memo[i] = None
-
-    def closecontainer(self, value):
-        if value:
-            del self.memo[id(value)]
-
-    def dump_array(self, value):
-        self.opencontainer(value)
-        write = self.write
+    def dump_array(self, value, write):
+        i = id(value)
+        if self.memo.has_key(i):
+            raise TypeError, "cannot marshal recursive sequences"
+        self.memo[i] = None
         dump = self.__dump
         write("<value><array><data>\n")
         for v in value:
-            dump(v)
+            dump(v, write)
         write("</data></array></value>\n")
-        self.closecontainer(value)
+        del self.memo[i]
     dispatch[TupleType] = dump_array
     dispatch[ListType] = dump_array
 
-    def dump_struct(self, value, escape=escape):
-        self.opencontainer(value)
-        write = self.write
+    def dump_struct(self, value, write, escape=escape):
+        i = id(value)
+        if self.memo.has_key(i):
+            raise TypeError, "cannot marshal recursive dictionaries"
+        self.memo[i] = None
         dump = self.__dump
         write("<value><struct>\n")
-        for k, v in value.items():
+        for k in value.keys():
             write("<member>\n")
             if type(k) is not StringType:
                 raise TypeError, "dictionary key must be string"
             write("<name>%s</name>\n" % escape(k))
-            dump(v)
+            dump(value[k], write)
             write("</member>\n")
         write("</struct></value>\n")
-        self.closecontainer(value)
+        del self.memo[i]
     dispatch[DictType] = dump_struct
 
-    def dump_instance(self, value):
+    def dump_instance(self, value, write):
         # check for special wrappers
         if value.__class__ in WRAPPERS:
+            self.write = write
             value.encode(self)
+            del self.write
         else:
             # store instance attributes as a struct (really?)
-            self.dump_struct(value.__dict__)
+            self.dump_struct(value.__dict__, write)
     dispatch[InstanceType] = dump_instance
 
+##
+# XML-RPC unmarshaller.
+#
+# @see loads
+
 class Unmarshaller:
     """Unmarshal an XML-RPC response, based on incoming XML event
     messages (start, data, end).  Call close() to get the resulting
@@ -679,7 +812,7 @@
     dispatch["dateTime.iso8601"] = end_dateTime
 
     def end_value(self, data):
-        # if we stumble upon an value element with no internal
+        # if we stumble upon a value element with no internal
         # elements, treat it as a string element
         if self._value:
             self.end_string(data)
@@ -704,6 +837,12 @@
 # --------------------------------------------------------------------
 # convenience functions
 
+##
+# Create a parser object, and connect it to an unmarshalling instance.
+# This function picks the fastest available XML parser.
+#
+# return A (parser, unmarshaller) tuple.
+
 def getparser():
     """getparser() -> parser, unmarshaller
 
@@ -711,7 +850,7 @@
     to an unmarshalling object.  Return both objects.
     """
     if FastParser and FastUnmarshaller:
-        target = FastUnmarshaller(True, False, binary, datetime)
+        target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
         parser = FastParser(target)
     else:
         target = Unmarshaller()
@@ -725,6 +864,19 @@
             parser = SlowParser(target)
     return parser, target
 
+##
+# Convert a Python tuple or a Fault instance to an XML-RPC packet.
+#
+# @def dumps(params, **options)
+# @param params A tuple or Fault instance.
+# @keyparam methodname If given, create a methodCall request for
+#     this method name.
+# @keyparam methodresponse If given, create a methodResponse packet.
+#     If used with a tuple, the tuple must be a singleton (that is,
+#     it must contain exactly one element).
+# @keyparam encoding The packet encoding.
+# @return A string containing marshalled data.
+
 def dumps(params, methodname=None, methodresponse=None, encoding=None):
     """data [,options] -> marshalled data
 
@@ -755,19 +907,23 @@
     elif methodresponse and isinstance(params, TupleType):
         assert len(params) == 1, "response tuple must be a singleton"
 
-    if encoding is None:
+    if not encoding:
         encoding = "utf-8"
 
-    m = Marshaller(encoding)
+    if FastMarshaller:
+        m = FastMarshaller(encoding)
+    else:
+        m = Marshaller(encoding)
+
     data = m.dumps(params)
 
     if encoding != "utf-8":
-        xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
+        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
     else:
         xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
 
     # standard XML-RPC wrappings
-    if methodname is not None:
+    if methodname:
         # a method call
         if not isinstance(methodname, StringType):
             methodname = methodname.encode(encoding)
@@ -790,6 +946,15 @@
         return data # return as is
     return string.join(data, "")
 
+##
+# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
+# represents a fault condition, this function raises a Fault exception.
+#
+# @param data An XML-RPC packet, given as an 8-bit string.
+# @return A tuple containing the the unpacked data, and the method name
+#     (None if not present).
+# @see Fault
+
 def loads(data):
     """data -> unmarshalled data, method name
 
@@ -799,6 +964,7 @@
     If the XML-RPC packet represents a fault condition, this function
     raises a Fault exception.
     """
+    import sys
     p, u = getparser()
     p.feed(data)
     p.close()
@@ -819,6 +985,11 @@
     def __call__(self, *args):
         return self.__send(self.__name, args)
 
+##
+# Standard transport class for XML-RPC over HTTP.
+# <p>
+# You can create custom transports by subclassing this method, and
+# overriding selected methods.
 
 class Transport:
     """Handles an HTTP transaction to an XML-RPC server."""
@@ -826,6 +997,15 @@
     # client identifier (may be overridden)
     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
 
+    ##
+    # Send a complete request, and parse the response.
+    #
+    # @param host Target host.
+    # @param handler Target PRC handler.
+    # @param request_body XML-RPC request body.
+    # @param verbose Debugging flag.
+    # @return Parsed response.
+
     def request(self, host, handler, request_body, verbose=0):
         # issue XML-RPC request
 
@@ -849,26 +1029,66 @@
 
         self.verbose = verbose
 
-        return self.parse_response(h.getfile())
+        try:
+            sock = h._conn.sock
+        except AttributeError:
+            sock = None
+
+        return self._parse_response(h.getfile(), sock)
+
+    ##
+    # Create parser.
+    #
+    # @return A 2-tuple containing a parser and a unmarshaller.
 
     def getparser(self):
         # get parser and unmarshaller
         return getparser()
 
+    ##
+    # Connect to server.
+    #
+    # @param host Target host.
+    # @return A connection handle.
+
     def make_connection(self, host):
         # create a HTTP connection object from a host descriptor
         import httplib
         return httplib.HTTP(host)
 
+    ##
+    # Send request header.
+    #
+    # @param connection Connection handle.
+    # @param handler Target RPC handler.
+    # @param request_body XML-RPC body.
+
     def send_request(self, connection, handler, request_body):
         connection.putrequest("POST", handler)
 
+    ##
+    # Send host name.
+    #
+    # @param connection Connection handle.
+    # @param host Host name.
+
     def send_host(self, connection, host):
         connection.putheader("Host", host)
 
+    ##
+    # Send user-agent identifier.
+    #
+    # @param connection Connection handle.
+
     def send_user_agent(self, connection):
         connection.putheader("User-Agent", self.user_agent)
 
+    ##
+    # Send request body.
+    #
+    # @param connection Connection handle.
+    # @param request_body XML-RPC request body.
+
     def send_content(self, connection, request_body):
         connection.putheader("Content-Type", "text/xml")
         connection.putheader("Content-Length", str(len(request_body)))
@@ -876,27 +1096,55 @@
         if request_body:
             connection.send(request_body)
 
-    def parse_response(self, f):
-        # read response from input file, and parse it
+    ##
+    # Parse response.
+    #
+    # @param file Stream.
+    # @return Response tuple and target method.
+
+    def parse_response(self, file):
+        # compatibility interface
+        return self._parse_response(file, None)
+
+    ##
+    # Parse response (alternate interface).  This is similar to the
+    # parse_response method, but also provides direct access to the
+    # underlying socket object (where available).
+    #
+    # @param file Stream.
+    # @param sock Socket handle (or None, if the socket object
+    #    could not be accessed).
+    # @return Response tuple and target method.
+
+    def _parse_response(self, file, sock):
+        # read response from input file/socket, and parse it
 
         p, u = self.getparser()
 
         while 1:
-            response = f.read(1024)
+            if sock:
+                response = sock.recv(1024)
+            else:
+                response = file.read(1024)
             if not response:
                 break
             if self.verbose:
                 print "body:", repr(response)
             p.feed(response)
 
-        f.close()
+        file.close()
         p.close()
 
         return u.close()
 
+##
+# Standard transport class for XML-RPC over HTTPS.
+
 class SafeTransport(Transport):
     """Handles an HTTPS transaction to an XML-RPC server."""
 
+    # FIXME: mostly untested
+
     def make_connection(self, host):
         # create a HTTPS connection object from a host descriptor
         # host may be a string, or a (host, x509-dict) tuple
@@ -918,6 +1166,23 @@
             host, x509 = host
         connection.putheader("Host", host)
 
+##
+# Standard server proxy.  This class establishes a virtual connection
+# to an XML-RPC server.
+# <p>
+# This class is available as ServerProxy and Server.  New code should
+# use ServerProxy, to avoid confusion.
+#
+# @def ServerProxy(uri, **options)
+# @param uri The connection point on the server.
+# @keyparam transport A transport factory, compatible with the
+#    standard transport class.
+# @keyparam encoding The default encoding used for 8-bit strings
+#    (default is UTF-8).
+# @keyparam verbose Use a true value to enable debugging output.
+#    (printed to standard output).
+# @see Transport
+
 class ServerProxy:
     """uri [,options] -> a logical connection to an XML-RPC server
 
@@ -995,6 +1260,7 @@
     # result getattr(server, "strange-python-name")(args)
 
 # compatibility
+
 Server = ServerProxy
 
 # --------------------------------------------------------------------