Home | History | Annotate | Download | only in Lib
      1 #

      2 # XML-RPC CLIENT LIBRARY

      3 # $Id$

      4 #

      5 # an XML-RPC client interface for Python.

      6 #

      7 # the marshalling and response parser code can also be used to

      8 # implement XML-RPC servers.

      9 #

     10 # Notes:

     11 # this version is designed to work with Python 2.1 or newer.

     12 #

     13 # History:

     14 # 1999-01-14 fl  Created

     15 # 1999-01-15 fl  Changed dateTime to use localtime

     16 # 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service

     17 # 1999-01-19 fl  Fixed array data element (from Skip Montanaro)

     18 # 1999-01-21 fl  Fixed dateTime constructor, etc.

     19 # 1999-02-02 fl  Added fault handling, handle empty sequences, etc.

     20 # 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)

     21 # 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)

     22 # 2000-11-28 fl  Changed boolean to check the truth value of its argument

     23 # 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches

     24 # 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)

     25 # 2001-03-28 fl  Make sure response tuple is a singleton

     26 # 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)

     27 # 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)

     28 # 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)

     29 # 2001-09-03 fl  Allow Transport subclass to override getparser

     30 # 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)

     31 # 2001-10-01 fl  Remove containers from memo cache when done with them

     32 # 2001-10-01 fl  Use faster escape method (80% dumps speedup)

     33 # 2001-10-02 fl  More dumps microtuning

     34 # 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)

     35 # 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow

     36 # 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)

     37 # 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)

     38 # 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)

     39 # 2002-04-07 fl  Added pythondoc comments

     40 # 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers

     41 # 2002-05-15 fl  Added error constants (from Andrew Kuchling)

     42 # 2002-06-27 fl  Merged with Python CVS version

     43 # 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)

     44 # 2003-01-22 sm  Add support for the bool type

     45 # 2003-02-27 gvr Remove apply calls

     46 # 2003-04-24 sm  Use cStringIO if available

     47 # 2003-04-25 ak  Add support for nil

     48 # 2003-06-15 gn  Add support for time.struct_time

     49 # 2003-07-12 gp  Correct marshalling of Faults

     50 # 2003-10-31 mvl Add multicall support

     51 # 2004-08-20 mvl Bump minimum supported Python version to 2.1

     52 #

     53 # Copyright (c) 1999-2002 by Secret Labs AB.

     54 # Copyright (c) 1999-2002 by Fredrik Lundh.

     55 #

     56 # info (at] pythonware.com

     57 # http://www.pythonware.com

     58 #

     59 # --------------------------------------------------------------------

     60 # The XML-RPC client interface is

     61 #

     62 # Copyright (c) 1999-2002 by Secret Labs AB

     63 # Copyright (c) 1999-2002 by Fredrik Lundh

     64 #

     65 # By obtaining, using, and/or copying this software and/or its

     66 # associated documentation, you agree that you have read, understood,

     67 # and will comply with the following terms and conditions:

     68 #

     69 # Permission to use, copy, modify, and distribute this software and

     70 # its associated documentation for any purpose and without fee is

     71 # hereby granted, provided that the above copyright notice appears in

     72 # all copies, and that both that copyright notice and this permission

     73 # notice appear in supporting documentation, and that the name of

     74 # Secret Labs AB or the author not be used in advertising or publicity

     75 # pertaining to distribution of the software without specific, written

     76 # prior permission.

     77 #

     78 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD

     79 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-

     80 # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR

     81 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY

     82 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,

     83 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS

     84 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE

     85 # OF THIS SOFTWARE.

     86 # --------------------------------------------------------------------

     87 
     88 #

     89 # things to look into some day:

     90 
     91 # TODO: sort out True/False/boolean issues for Python 2.3

     92 
     93 """
     94 An XML-RPC client interface for Python.
     95 
     96 The marshalling and response parser code can also be used to
     97 implement XML-RPC servers.
     98 
     99 Exported exceptions:
    100 
    101   Error          Base class for client errors
    102   ProtocolError  Indicates an HTTP protocol error
    103   ResponseError  Indicates a broken response package
    104   Fault          Indicates an XML-RPC fault package
    105 
    106 Exported classes:
    107 
    108   ServerProxy    Represents a logical connection to an XML-RPC server
    109 
    110   MultiCall      Executor of boxcared xmlrpc requests
    111   Boolean        boolean wrapper to generate a "boolean" XML-RPC value
    112   DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
    113                  localtime integer value to generate a "dateTime.iso8601"
    114                  XML-RPC value
    115   Binary         binary data wrapper
    116 
    117   SlowParser     Slow but safe standard parser (based on xmllib)
    118   Marshaller     Generate an XML-RPC params chunk from a Python data structure
    119   Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
    120   Transport      Handles an HTTP transaction to an XML-RPC server
    121   SafeTransport  Handles an HTTPS transaction to an XML-RPC server
    122 
    123 Exported constants:
    124 
    125   True
    126   False
    127 
    128 Exported functions:
    129 
    130   boolean        Convert any Python value to an XML-RPC boolean
    131   getparser      Create instance of the fastest available parser & attach
    132                  to an unmarshalling object
    133   dumps          Convert an argument tuple or a Fault instance to an XML-RPC
    134                  request (or response, if the methodresponse option is used).
    135   loads          Convert an XML-RPC packet to unmarshalled data plus a method
    136                  name (None if not present).
    137 """
    138 
    139 import re, string, time, operator
    140 
    141 from types import *
    142 import socket
    143 import errno
    144 import httplib
    145 try:
    146     import gzip
    147 except ImportError:
    148     gzip = None #python can be built without zlib/gzip support

    149 
    150 # --------------------------------------------------------------------

    151 # Internal stuff

    152 
    153 try:
    154     unicode
    155 except NameError:
    156     unicode = None # unicode support not available

    157 
    158 try:
    159     import datetime
    160 except ImportError:
    161     datetime = None
    162 
    163 try:
    164     _bool_is_builtin = False.__class__.__name__ == "bool"
    165 except NameError:
    166     _bool_is_builtin = 0
    167 
    168 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
    169     # decode non-ascii string (if possible)

    170     if unicode and encoding and is8bit(data):
    171         data = unicode(data, encoding)
    172     return data
    173 
    174 def escape(s, replace=string.replace):
    175     s = replace(s, "&", "&")
    176     s = replace(s, "<", "&lt;")
    177     return replace(s, ">", "&gt;",)
    178 
    179 if unicode:
    180     def _stringify(string):
    181         # convert to 7-bit ascii if possible

    182         try:
    183             return string.encode("ascii")
    184         except UnicodeError:
    185             return string
    186 else:
    187     def _stringify(string):
    188         return string
    189 
    190 __version__ = "1.0.1"
    191 
    192 # xmlrpc integer limits

    193 MAXINT =  2L**31-1
    194 MININT = -2L**31
    195 
    196 # --------------------------------------------------------------------

    197 # Error constants (from Dan Libby's specification at

    198 # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)

    199 
    200 # Ranges of errors

    201 PARSE_ERROR       = -32700
    202 SERVER_ERROR      = -32600
    203 APPLICATION_ERROR = -32500
    204 SYSTEM_ERROR      = -32400
    205 TRANSPORT_ERROR   = -32300
    206 
    207 # Specific errors

    208 NOT_WELLFORMED_ERROR  = -32700
    209 UNSUPPORTED_ENCODING  = -32701
    210 INVALID_ENCODING_CHAR = -32702
    211 INVALID_XMLRPC        = -32600
    212 METHOD_NOT_FOUND      = -32601
    213 INVALID_METHOD_PARAMS = -32602
    214 INTERNAL_ERROR        = -32603
    215 
    216 # --------------------------------------------------------------------

    217 # Exceptions

    218 
    219 ##

    220 # Base class for all kinds of client-side errors.

    221 
    222 class Error(Exception):
    223     """Base class for client errors."""
    224     def __str__(self):
    225         return repr(self)
    226 
    227 ##

    228 # Indicates an HTTP-level protocol error.  This is raised by the HTTP

    229 # transport layer, if the server returns an error code other than 200

    230 # (OK).

    231 #

    232 # @param url The target URL.

    233 # @param errcode The HTTP error code.

    234 # @param errmsg The HTTP error message.

    235 # @param headers The HTTP header dictionary.

    236 
    237 class ProtocolError(Error):
    238     """Indicates an HTTP protocol error."""
    239     def __init__(self, url, errcode, errmsg, headers):
    240         Error.__init__(self)
    241         self.url = url
    242         self.errcode = errcode
    243         self.errmsg = errmsg
    244         self.headers = headers
    245     def __repr__(self):
    246         return (
    247             "<ProtocolError for %s: %s %s>" %
    248             (self.url, self.errcode, self.errmsg)
    249             )
    250 
    251 ##

    252 # Indicates a broken XML-RPC response package.  This exception is

    253 # raised by the unmarshalling layer, if the XML-RPC response is

    254 # malformed.

    255 
    256 class ResponseError(Error):
    257     """Indicates a broken response package."""
    258     pass
    259 
    260 ##

    261 # Indicates an XML-RPC fault response package.  This exception is

    262 # raised by the unmarshalling layer, if the XML-RPC response contains

    263 # a fault string.  This exception can also used as a class, to

    264 # generate a fault XML-RPC message.

    265 #

    266 # @param faultCode The XML-RPC fault code.

    267 # @param faultString The XML-RPC fault string.

    268 
    269 class Fault(Error):
    270     """Indicates an XML-RPC fault package."""
    271     def __init__(self, faultCode, faultString, **extra):
    272         Error.__init__(self)
    273         self.faultCode = faultCode
    274         self.faultString = faultString
    275     def __repr__(self):
    276         return (
    277             "<Fault %s: %s>" %
    278             (self.faultCode, repr(self.faultString))
    279             )
    280 
    281 # --------------------------------------------------------------------

    282 # Special values

    283 
    284 ##

    285 # Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and

    286 # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to

    287 # generate boolean XML-RPC values.

    288 #

    289 # @param value A boolean value.  Any true value is interpreted as True,

    290 #              all other values are interpreted as False.

    291 
    292 from sys import modules
    293 mod_dict = modules[__name__].__dict__
    294 if _bool_is_builtin:
    295     boolean = Boolean = bool
    296     # to avoid breaking code which references xmlrpclib.{True,False}

    297     mod_dict['True'] = True
    298     mod_dict['False'] = False
    299 else:
    300     class Boolean:
    301         """Boolean-value wrapper.
    302 
    303         Use True or False to generate a "boolean" XML-RPC value.
    304         """
    305 
    306         def __init__(self, value = 0):
    307             self.value = operator.truth(value)
    308 
    309         def encode(self, out):
    310             out.write("<value><boolean>%d</boolean></value>\n" % self.value)
    311 
    312         def __cmp__(self, other):
    313             if isinstance(other, Boolean):
    314                 other = other.value
    315             return cmp(self.value, other)
    316 
    317         def __repr__(self):
    318             if self.value:
    319                 return "<Boolean True at %x>" % id(self)
    320             else:
    321                 return "<Boolean False at %x>" % id(self)
    322 
    323         def __int__(self):
    324             return self.value
    325 
    326         def __nonzero__(self):
    327             return self.value
    328 
    329     mod_dict['True'] = Boolean(1)
    330     mod_dict['False'] = Boolean(0)
    331 
    332     ##

    333     # Map true or false value to XML-RPC boolean values.

    334     #

    335     # @def boolean(value)

    336     # @param value A boolean value.  Any true value is mapped to True,

    337     #              all other values are mapped to False.

    338     # @return xmlrpclib.True or xmlrpclib.False.

    339     # @see Boolean

    340     # @see True

    341     # @see False

    342 
    343     def boolean(value, _truefalse=(False, True)):
    344         """Convert any Python value to XML-RPC 'boolean'."""
    345         return _truefalse[operator.truth(value)]
    346 
    347 del modules, mod_dict
    348 
    349 ##

    350 # Wrapper for XML-RPC DateTime values.  This converts a time value to

    351 # the format used by XML-RPC.

    352 # <p>

    353 # The value can be given as a string in the format

    354 # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by

    355 # time.localtime()), or an integer value (as returned by time.time()).

    356 # The wrapper uses time.localtime() to convert an integer to a time

    357 # tuple.

    358 #

    359 # @param value The time, given as an ISO 8601 string, a time

    360 #              tuple, or a integer time value.

    361 
    362 def _strftime(value):
    363     if datetime:
    364         if isinstance(value, datetime.datetime):
    365             return "%04d%02d%02dT%02d:%02d:%02d" % (
    366                 value.year, value.month, value.day,
    367                 value.hour, value.minute, value.second)
    368 
    369     if not isinstance(value, (TupleType, time.struct_time)):
    370         if value == 0:
    371             value = time.time()
    372         value = time.localtime(value)
    373 
    374     return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
    375 
    376 class DateTime:
    377     """DateTime wrapper for an ISO 8601 string or time tuple or
    378     localtime integer value to generate 'dateTime.iso8601' XML-RPC
    379     value.
    380     """
    381 
    382     def __init__(self, value=0):
    383         if isinstance(value, StringType):
    384             self.value = value
    385         else:
    386             self.value = _strftime(value)
    387 
    388     def make_comparable(self, other):
    389         if isinstance(other, DateTime):
    390             s = self.value
    391             o = other.value
    392         elif datetime and isinstance(other, datetime.datetime):
    393             s = self.value
    394             o = other.strftime("%Y%m%dT%H:%M:%S")
    395         elif isinstance(other, (str, unicode)):
    396             s = self.value
    397             o = other
    398         elif hasattr(other, "timetuple"):
    399             s = self.timetuple()
    400             o = other.timetuple()
    401         else:
    402             otype = (hasattr(other, "__class__")
    403                      and other.__class__.__name__
    404                      or type(other))
    405             raise TypeError("Can't compare %s and %s" %
    406                             (self.__class__.__name__, otype))
    407         return s, o
    408 
    409     def __lt__(self, other):
    410         s, o = self.make_comparable(other)
    411         return s < o
    412 
    413     def __le__(self, other):
    414         s, o = self.make_comparable(other)
    415         return s <= o
    416 
    417     def __gt__(self, other):
    418         s, o = self.make_comparable(other)
    419         return s > o
    420 
    421     def __ge__(self, other):
    422         s, o = self.make_comparable(other)
    423         return s >= o
    424 
    425     def __eq__(self, other):
    426         s, o = self.make_comparable(other)
    427         return s == o
    428 
    429     def __ne__(self, other):
    430         s, o = self.make_comparable(other)
    431         return s != o
    432 
    433     def timetuple(self):
    434         return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
    435 
    436     def __cmp__(self, other):
    437         s, o = self.make_comparable(other)
    438         return cmp(s, o)
    439 
    440     ##

    441     # Get date/time value.

    442     #

    443     # @return Date/time value, as an ISO 8601 string.

    444 
    445     def __str__(self):
    446         return self.value
    447 
    448     def __repr__(self):
    449         return "<DateTime %s at %x>" % (repr(self.value), id(self))
    450 
    451     def decode(self, data):
    452         data = str(data)
    453         self.value = string.strip(data)
    454 
    455     def encode(self, out):
    456         out.write("<value><dateTime.iso8601>")
    457         out.write(self.value)
    458         out.write("</dateTime.iso8601></value>\n")
    459 
    460 def _datetime(data):
    461     # decode xml element contents into a DateTime structure.

    462     value = DateTime()
    463     value.decode(data)
    464     return value
    465 
    466 def _datetime_type(data):
    467     t = time.strptime(data, "%Y%m%dT%H:%M:%S")
    468     return datetime.datetime(*tuple(t)[:6])
    469 
    470 ##

    471 # Wrapper for binary data.  This can be used to transport any kind

    472 # of binary data over XML-RPC, using BASE64 encoding.

    473 #

    474 # @param data An 8-bit string containing arbitrary data.

    475 
    476 import base64
    477 try:
    478     import cStringIO as StringIO
    479 except ImportError:
    480     import StringIO
    481 
    482 class Binary:
    483     """Wrapper for binary data."""
    484 
    485     def __init__(self, data=None):
    486         self.data = data
    487 
    488     ##

    489     # Get buffer contents.

    490     #

    491     # @return Buffer contents, as an 8-bit string.

    492 
    493     def __str__(self):
    494         return self.data or ""
    495 
    496     def __cmp__(self, other):
    497         if isinstance(other, Binary):
    498             other = other.data
    499         return cmp(self.data, other)
    500 
    501     def decode(self, data):
    502         self.data = base64.decodestring(data)
    503 
    504     def encode(self, out):
    505         out.write("<value><base64>\n")
    506         base64.encode(StringIO.StringIO(self.data), out)
    507         out.write("</base64></value>\n")
    508 
    509 def _binary(data):
    510     # decode xml element contents into a Binary structure

    511     value = Binary()
    512     value.decode(data)
    513     return value
    514 
    515 WRAPPERS = (DateTime, Binary)
    516 if not _bool_is_builtin:
    517     WRAPPERS = WRAPPERS + (Boolean,)
    518 
    519 # --------------------------------------------------------------------

    520 # XML parsers

    521 
    522 try:
    523     # optional xmlrpclib accelerator

    524     import _xmlrpclib
    525     FastParser = _xmlrpclib.Parser
    526     FastUnmarshaller = _xmlrpclib.Unmarshaller
    527 except (AttributeError, ImportError):
    528     FastParser = FastUnmarshaller = None
    529 
    530 try:
    531     import _xmlrpclib
    532     FastMarshaller = _xmlrpclib.Marshaller
    533 except (AttributeError, ImportError):
    534     FastMarshaller = None
    535 
    536 try:
    537     from xml.parsers import expat
    538     if not hasattr(expat, "ParserCreate"):
    539         raise ImportError
    540 except ImportError:
    541     ExpatParser = None # expat not available

    542 else:
    543     class ExpatParser:
    544         # fast expat parser for Python 2.0 and later.

    545         def __init__(self, target):
    546             self._parser = parser = expat.ParserCreate(None, None)
    547             self._target = target
    548             parser.StartElementHandler = target.start
    549             parser.EndElementHandler = target.end
    550             parser.CharacterDataHandler = target.data
    551             encoding = None
    552             if not parser.returns_unicode:
    553                 encoding = "utf-8"
    554             target.xml(encoding, None)
    555 
    556         def feed(self, data):
    557             self._parser.Parse(data, 0)
    558 
    559         def close(self):
    560             self._parser.Parse("", 1) # end of data

    561             del self._target, self._parser # get rid of circular references

    562 
    563 class SlowParser:
    564     """Default XML parser (based on xmllib.XMLParser)."""
    565     # this is the slowest parser.

    566     def __init__(self, target):
    567         import xmllib # lazy subclassing (!)

    568         if xmllib.XMLParser not in SlowParser.__bases__:
    569             SlowParser.__bases__ = (xmllib.XMLParser,)
    570         self.handle_xml = target.xml
    571         self.unknown_starttag = target.start
    572         self.handle_data = target.data
    573         self.handle_cdata = target.data
    574         self.unknown_endtag = target.end
    575         try:
    576             xmllib.XMLParser.__init__(self, accept_utf8=1)
    577         except TypeError:
    578             xmllib.XMLParser.__init__(self) # pre-2.0

    579 
    580 # --------------------------------------------------------------------

    581 # XML-RPC marshalling and unmarshalling code

    582 
    583 ##

    584 # XML-RPC marshaller.

    585 #

    586 # @param encoding Default encoding for 8-bit strings.  The default

    587 #     value is None (interpreted as UTF-8).

    588 # @see dumps

    589 
    590 class Marshaller:
    591     """Generate an XML-RPC params chunk from a Python data structure.
    592 
    593     Create a Marshaller instance for each set of parameters, and use
    594     the "dumps" method to convert your data (represented as a tuple)
    595     to an XML-RPC params chunk.  To write a fault response, pass a
    596     Fault instance instead.  You may prefer to use the "dumps" module
    597     function for this purpose.
    598     """
    599 
    600     # by the way, if you don't understand what's going on in here,

    601     # that's perfectly ok.

    602 
    603     def __init__(self, encoding=None, allow_none=0):
    604         self.memo = {}
    605         self.data = None
    606         self.encoding = encoding
    607         self.allow_none = allow_none
    608 
    609     dispatch = {}
    610 
    611     def dumps(self, values):
    612         out = []
    613         write = out.append
    614         dump = self.__dump
    615         if isinstance(values, Fault):
    616             # fault instance

    617             write("<fault>\n")
    618             dump({'faultCode': values.faultCode,
    619                   'faultString': values.faultString},
    620                  write)
    621             write("</fault>\n")
    622         else:
    623             # parameter block

    624             # FIXME: the xml-rpc specification allows us to leave out

    625             # the entire <params> block if there are no parameters.

    626             # however, changing this may break older code (including

    627             # old versions of xmlrpclib.py), so this is better left as

    628             # is for now.  See @XMLRPC3 for more information. /F

    629             write("<params>\n")
    630             for v in values:
    631                 write("<param>\n")
    632                 dump(v, write)
    633                 write("</param>\n")
    634             write("</params>\n")
    635         result = string.join(out, "")
    636         return result
    637 
    638     def __dump(self, value, write):
    639         try:
    640             f = self.dispatch[type(value)]
    641         except KeyError:
    642             # check if this object can be marshalled as a structure

    643             try:
    644                 value.__dict__
    645             except:
    646                 raise TypeError, "cannot marshal %s objects" % type(value)
    647             # check if this class is a sub-class of a basic type,

    648             # because we don't know how to marshal these types

    649             # (e.g. a string sub-class)

    650             for type_ in type(value).__mro__:
    651                 if type_ in self.dispatch.keys():
    652                     raise TypeError, "cannot marshal %s objects" % type(value)
    653             f = self.dispatch[InstanceType]
    654         f(self, value, write)
    655 
    656     def dump_nil (self, value, write):
    657         if not self.allow_none:
    658             raise TypeError, "cannot marshal None unless allow_none is enabled"
    659         write("<value><nil/></value>")
    660     dispatch[NoneType] = dump_nil
    661 
    662     def dump_int(self, value, write):
    663         # in case ints are > 32 bits

    664         if value > MAXINT or value < MININT:
    665             raise OverflowError, "int exceeds XML-RPC limits"
    666         write("<value><int>")
    667         write(str(value))
    668         write("</int></value>\n")
    669     dispatch[IntType] = dump_int
    670 
    671     if _bool_is_builtin:
    672         def dump_bool(self, value, write):
    673             write("<value><boolean>")
    674             write(value and "1" or "0")
    675             write("</boolean></value>\n")
    676         dispatch[bool] = dump_bool
    677 
    678     def dump_long(self, value, write):
    679         if value > MAXINT or value < MININT:
    680             raise OverflowError, "long int exceeds XML-RPC limits"
    681         write("<value><int>")
    682         write(str(int(value)))
    683         write("</int></value>\n")
    684     dispatch[LongType] = dump_long
    685 
    686     def dump_double(self, value, write):
    687         write("<value><double>")
    688         write(repr(value))
    689         write("</double></value>\n")
    690     dispatch[FloatType] = dump_double
    691 
    692     def dump_string(self, value, write, escape=escape):
    693         write("<value><string>")
    694         write(escape(value))
    695         write("</string></value>\n")
    696     dispatch[StringType] = dump_string
    697 
    698     if unicode:
    699         def dump_unicode(self, value, write, escape=escape):
    700             value = value.encode(self.encoding)
    701             write("<value><string>")
    702             write(escape(value))
    703             write("</string></value>\n")
    704         dispatch[UnicodeType] = dump_unicode
    705 
    706     def dump_array(self, value, write):
    707         i = id(value)
    708         if i in self.memo:
    709             raise TypeError, "cannot marshal recursive sequences"
    710         self.memo[i] = None
    711         dump = self.__dump
    712         write("<value><array><data>\n")
    713         for v in value:
    714             dump(v, write)
    715         write("</data></array></value>\n")
    716         del self.memo[i]
    717     dispatch[TupleType] = dump_array
    718     dispatch[ListType] = dump_array
    719 
    720     def dump_struct(self, value, write, escape=escape):
    721         i = id(value)
    722         if i in self.memo:
    723             raise TypeError, "cannot marshal recursive dictionaries"
    724         self.memo[i] = None
    725         dump = self.__dump
    726         write("<value><struct>\n")
    727         for k, v in value.items():
    728             write("<member>\n")
    729             if type(k) is not StringType:
    730                 if unicode and type(k) is UnicodeType:
    731                     k = k.encode(self.encoding)
    732                 else:
    733                     raise TypeError, "dictionary key must be string"
    734             write("<name>%s</name>\n" % escape(k))
    735             dump(v, write)
    736             write("</member>\n")
    737         write("</struct></value>\n")
    738         del self.memo[i]
    739     dispatch[DictType] = dump_struct
    740 
    741     if datetime:
    742         def dump_datetime(self, value, write):
    743             write("<value><dateTime.iso8601>")
    744             write(_strftime(value))
    745             write("</dateTime.iso8601></value>\n")
    746         dispatch[datetime.datetime] = dump_datetime
    747 
    748     def dump_instance(self, value, write):
    749         # check for special wrappers

    750         if value.__class__ in WRAPPERS:
    751             self.write = write
    752             value.encode(self)
    753             del self.write
    754         else:
    755             # store instance attributes as a struct (really?)

    756             self.dump_struct(value.__dict__, write)
    757     dispatch[InstanceType] = dump_instance
    758 
    759 ##

    760 # XML-RPC unmarshaller.

    761 #

    762 # @see loads

    763 
    764 class Unmarshaller:
    765     """Unmarshal an XML-RPC response, based on incoming XML event
    766     messages (start, data, end).  Call close() to get the resulting
    767     data structure.
    768 
    769     Note that this reader is fairly tolerant, and gladly accepts bogus
    770     XML-RPC data without complaining (but not bogus XML).
    771     """
    772 
    773     # and again, if you don't understand what's going on in here,

    774     # that's perfectly ok.

    775 
    776     def __init__(self, use_datetime=0):
    777         self._type = None
    778         self._stack = []
    779         self._marks = []
    780         self._data = []
    781         self._methodname = None
    782         self._encoding = "utf-8"
    783         self.append = self._stack.append
    784         self._use_datetime = use_datetime
    785         if use_datetime and not datetime:
    786             raise ValueError, "the datetime module is not available"
    787 
    788     def close(self):
    789         # return response tuple and target method

    790         if self._type is None or self._marks:
    791             raise ResponseError()
    792         if self._type == "fault":
    793             raise Fault(**self._stack[0])
    794         return tuple(self._stack)
    795 
    796     def getmethodname(self):
    797         return self._methodname
    798 
    799     #

    800     # event handlers

    801 
    802     def xml(self, encoding, standalone):
    803         self._encoding = encoding
    804         # FIXME: assert standalone == 1 ???

    805 
    806     def start(self, tag, attrs):
    807         # prepare to handle this element

    808         if tag == "array" or tag == "struct":
    809             self._marks.append(len(self._stack))
    810         self._data = []
    811         self._value = (tag == "value")
    812 
    813     def data(self, text):
    814         self._data.append(text)
    815 
    816     def end(self, tag, join=string.join):
    817         # call the appropriate end tag handler

    818         try:
    819             f = self.dispatch[tag]
    820         except KeyError:
    821             pass # unknown tag ?

    822         else:
    823             return f(self, join(self._data, ""))
    824 
    825     #

    826     # accelerator support

    827 
    828     def end_dispatch(self, tag, data):
    829         # dispatch data

    830         try:
    831             f = self.dispatch[tag]
    832         except KeyError:
    833             pass # unknown tag ?

    834         else:
    835             return f(self, data)
    836 
    837     #

    838     # element decoders

    839 
    840     dispatch = {}
    841 
    842     def end_nil (self, data):
    843         self.append(None)
    844         self._value = 0
    845     dispatch["nil"] = end_nil
    846 
    847     def end_boolean(self, data):
    848         if data == "0":
    849             self.append(False)
    850         elif data == "1":
    851             self.append(True)
    852         else:
    853             raise TypeError, "bad boolean value"
    854         self._value = 0
    855     dispatch["boolean"] = end_boolean
    856 
    857     def end_int(self, data):
    858         self.append(int(data))
    859         self._value = 0
    860     dispatch["i4"] = end_int
    861     dispatch["i8"] = end_int
    862     dispatch["int"] = end_int
    863 
    864     def end_double(self, data):
    865         self.append(float(data))
    866         self._value = 0
    867     dispatch["double"] = end_double
    868 
    869     def end_string(self, data):
    870         if self._encoding:
    871             data = _decode(data, self._encoding)
    872         self.append(_stringify(data))
    873         self._value = 0
    874     dispatch["string"] = end_string
    875     dispatch["name"] = end_string # struct keys are always strings

    876 
    877     def end_array(self, data):
    878         mark = self._marks.pop()
    879         # map arrays to Python lists

    880         self._stack[mark:] = [self._stack[mark:]]
    881         self._value = 0
    882     dispatch["array"] = end_array
    883 
    884     def end_struct(self, data):
    885         mark = self._marks.pop()
    886         # map structs to Python dictionaries

    887         dict = {}
    888         items = self._stack[mark:]
    889         for i in range(0, len(items), 2):
    890             dict[_stringify(items[i])] = items[i+1]
    891         self._stack[mark:] = [dict]
    892         self._value = 0
    893     dispatch["struct"] = end_struct
    894 
    895     def end_base64(self, data):
    896         value = Binary()
    897         value.decode(data)
    898         self.append(value)
    899         self._value = 0
    900     dispatch["base64"] = end_base64
    901 
    902     def end_dateTime(self, data):
    903         value = DateTime()
    904         value.decode(data)
    905         if self._use_datetime:
    906             value = _datetime_type(data)
    907         self.append(value)
    908     dispatch["dateTime.iso8601"] = end_dateTime
    909 
    910     def end_value(self, data):
    911         # if we stumble upon a value element with no internal

    912         # elements, treat it as a string element

    913         if self._value:
    914             self.end_string(data)
    915     dispatch["value"] = end_value
    916 
    917     def end_params(self, data):
    918         self._type = "params"
    919     dispatch["params"] = end_params
    920 
    921     def end_fault(self, data):
    922         self._type = "fault"
    923     dispatch["fault"] = end_fault
    924 
    925     def end_methodName(self, data):
    926         if self._encoding:
    927             data = _decode(data, self._encoding)
    928         self._methodname = data
    929         self._type = "methodName" # no params

    930     dispatch["methodName"] = end_methodName
    931 
    932 ## Multicall support

    933 #

    934 
    935 class _MultiCallMethod:
    936     # some lesser magic to store calls made to a MultiCall object

    937     # for batch execution

    938     def __init__(self, call_list, name):
    939         self.__call_list = call_list
    940         self.__name = name
    941     def __getattr__(self, name):
    942         return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
    943     def __call__(self, *args):
    944         self.__call_list.append((self.__name, args))
    945 
    946 class MultiCallIterator:
    947     """Iterates over the results of a multicall. Exceptions are
    948     thrown in response to xmlrpc faults."""
    949 
    950     def __init__(self, results):
    951         self.results = results
    952 
    953     def __getitem__(self, i):
    954         item = self.results[i]
    955         if type(item) == type({}):
    956             raise Fault(item['faultCode'], item['faultString'])
    957         elif type(item) == type([]):
    958             return item[0]
    959         else:
    960             raise ValueError,\
    961                   "unexpected type in multicall result"
    962 
    963 class MultiCall:
    964     """server -> a object used to boxcar method calls
    965 
    966     server should be a ServerProxy object.
    967 
    968     Methods can be added to the MultiCall using normal
    969     method call syntax e.g.:
    970 
    971     multicall = MultiCall(server_proxy)
    972     multicall.add(2,3)
    973     multicall.get_address("Guido")
    974 
    975     To execute the multicall, call the MultiCall object e.g.:
    976 
    977     add_result, address = multicall()
    978     """
    979 
    980     def __init__(self, server):
    981         self.__server = server
    982         self.__call_list = []
    983 
    984     def __repr__(self):
    985         return "<MultiCall at %x>" % id(self)
    986 
    987     __str__ = __repr__
    988 
    989     def __getattr__(self, name):
    990         return _MultiCallMethod(self.__call_list, name)
    991 
    992     def __call__(self):
    993         marshalled_list = []
    994         for name, args in self.__call_list:
    995             marshalled_list.append({'methodName' : name, 'params' : args})
    996 
    997         return MultiCallIterator(self.__server.system.multicall(marshalled_list))
    998 
    999 # --------------------------------------------------------------------

   1000 # convenience functions

   1001 
   1002 ##

   1003 # Create a parser object, and connect it to an unmarshalling instance.

   1004 # This function picks the fastest available XML parser.

   1005 #

   1006 # return A (parser, unmarshaller) tuple.

   1007 
   1008 def getparser(use_datetime=0):
   1009     """getparser() -> parser, unmarshaller
   1010 
   1011     Create an instance of the fastest available parser, and attach it
   1012     to an unmarshalling object.  Return both objects.
   1013     """
   1014     if use_datetime and not datetime:
   1015         raise ValueError, "the datetime module is not available"
   1016     if FastParser and FastUnmarshaller:
   1017         if use_datetime:
   1018             mkdatetime = _datetime_type
   1019         else:
   1020             mkdatetime = _datetime
   1021         target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
   1022         parser = FastParser(target)
   1023     else:
   1024         target = Unmarshaller(use_datetime=use_datetime)
   1025         if FastParser:
   1026             parser = FastParser(target)
   1027         elif ExpatParser:
   1028             parser = ExpatParser(target)
   1029         else:
   1030             parser = SlowParser(target)
   1031     return parser, target
   1032 
   1033 ##

   1034 # Convert a Python tuple or a Fault instance to an XML-RPC packet.

   1035 #

   1036 # @def dumps(params, **options)

   1037 # @param params A tuple or Fault instance.

   1038 # @keyparam methodname If given, create a methodCall request for

   1039 #     this method name.

   1040 # @keyparam methodresponse If given, create a methodResponse packet.

   1041 #     If used with a tuple, the tuple must be a singleton (that is,

   1042 #     it must contain exactly one element).

   1043 # @keyparam encoding The packet encoding.

   1044 # @return A string containing marshalled data.

   1045 
   1046 def dumps(params, methodname=None, methodresponse=None, encoding=None,
   1047           allow_none=0):
   1048     """data [,options] -> marshalled data
   1049 
   1050     Convert an argument tuple or a Fault instance to an XML-RPC
   1051     request (or response, if the methodresponse option is used).
   1052 
   1053     In addition to the data object, the following options can be given
   1054     as keyword arguments:
   1055 
   1056         methodname: the method name for a methodCall packet
   1057 
   1058         methodresponse: true to create a methodResponse packet.
   1059         If this option is used with a tuple, the tuple must be
   1060         a singleton (i.e. it can contain only one element).
   1061 
   1062         encoding: the packet encoding (default is UTF-8)
   1063 
   1064     All 8-bit strings in the data structure are assumed to use the
   1065     packet encoding.  Unicode strings are automatically converted,
   1066     where necessary.
   1067     """
   1068 
   1069     assert isinstance(params, TupleType) or isinstance(params, Fault),\
   1070            "argument must be tuple or Fault instance"
   1071 
   1072     if isinstance(params, Fault):
   1073         methodresponse = 1
   1074     elif methodresponse and isinstance(params, TupleType):
   1075         assert len(params) == 1, "response tuple must be a singleton"
   1076 
   1077     if not encoding:
   1078         encoding = "utf-8"
   1079 
   1080     if FastMarshaller:
   1081         m = FastMarshaller(encoding)
   1082     else:
   1083         m = Marshaller(encoding, allow_none)
   1084 
   1085     data = m.dumps(params)
   1086 
   1087     if encoding != "utf-8":
   1088         xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
   1089     else:
   1090         xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default

   1091 
   1092     # standard XML-RPC wrappings

   1093     if methodname:
   1094         # a method call

   1095         if not isinstance(methodname, StringType):
   1096             methodname = methodname.encode(encoding)
   1097         data = (
   1098             xmlheader,
   1099             "<methodCall>\n"
   1100             "<methodName>", methodname, "</methodName>\n",
   1101             data,
   1102             "</methodCall>\n"
   1103             )
   1104     elif methodresponse:
   1105         # a method response, or a fault structure

   1106         data = (
   1107             xmlheader,
   1108             "<methodResponse>\n",
   1109             data,
   1110             "</methodResponse>\n"
   1111             )
   1112     else:
   1113         return data # return as is

   1114     return string.join(data, "")
   1115 
   1116 ##

   1117 # Convert an XML-RPC packet to a Python object.  If the XML-RPC packet

   1118 # represents a fault condition, this function raises a Fault exception.

   1119 #

   1120 # @param data An XML-RPC packet, given as an 8-bit string.

   1121 # @return A tuple containing the unpacked data, and the method name

   1122 #     (None if not present).

   1123 # @see Fault

   1124 
   1125 def loads(data, use_datetime=0):
   1126     """data -> unmarshalled data, method name
   1127 
   1128     Convert an XML-RPC packet to unmarshalled data plus a method
   1129     name (None if not present).
   1130 
   1131     If the XML-RPC packet represents a fault condition, this function
   1132     raises a Fault exception.
   1133     """
   1134     p, u = getparser(use_datetime=use_datetime)
   1135     p.feed(data)
   1136     p.close()
   1137     return u.close(), u.getmethodname()
   1138 
   1139 ##

   1140 # Encode a string using the gzip content encoding such as specified by the

   1141 # Content-Encoding: gzip

   1142 # in the HTTP header, as described in RFC 1952

   1143 #

   1144 # @param data the unencoded data

   1145 # @return the encoded data

   1146 
   1147 def gzip_encode(data):
   1148     """data -> gzip encoded data
   1149 
   1150     Encode data using the gzip content encoding as described in RFC 1952
   1151     """
   1152     if not gzip:
   1153         raise NotImplementedError
   1154     f = StringIO.StringIO()
   1155     gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
   1156     gzf.write(data)
   1157     gzf.close()
   1158     encoded = f.getvalue()
   1159     f.close()
   1160     return encoded
   1161 
   1162 ##

   1163 # Decode a string using the gzip content encoding such as specified by the

   1164 # Content-Encoding: gzip

   1165 # in the HTTP header, as described in RFC 1952

   1166 #

   1167 # @param data The encoded data

   1168 # @return the unencoded data

   1169 # @raises ValueError if data is not correctly coded.

   1170 
   1171 def gzip_decode(data):
   1172     """gzip encoded data -> unencoded data
   1173 
   1174     Decode data using the gzip content encoding as described in RFC 1952
   1175     """
   1176     if not gzip:
   1177         raise NotImplementedError
   1178     f = StringIO.StringIO(data)
   1179     gzf = gzip.GzipFile(mode="rb", fileobj=f)
   1180     try:
   1181         decoded = gzf.read()
   1182     except IOError:
   1183         raise ValueError("invalid data")
   1184     f.close()
   1185     gzf.close()
   1186     return decoded
   1187 
   1188 ##

   1189 # Return a decoded file-like object for the gzip encoding

   1190 # as described in RFC 1952.

   1191 #

   1192 # @param response A stream supporting a read() method

   1193 # @return a file-like object that the decoded data can be read() from

   1194 
   1195 class GzipDecodedResponse(gzip.GzipFile if gzip else object):
   1196     """a file-like object to decode a response encoded with the gzip
   1197     method, as described in RFC 1952.
   1198     """
   1199     def __init__(self, response):
   1200         #response doesn't support tell() and read(), required by

   1201         #GzipFile

   1202         if not gzip:
   1203             raise NotImplementedError
   1204         self.stringio = StringIO.StringIO(response.read())
   1205         gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
   1206 
   1207     def close(self):
   1208         gzip.GzipFile.close(self)
   1209         self.stringio.close()
   1210 
   1211 
   1212 # --------------------------------------------------------------------

   1213 # request dispatcher

   1214 
   1215 class _Method:
   1216     # some magic to bind an XML-RPC method to an RPC server.

   1217     # supports "nested" methods (e.g. examples.getStateName)

   1218     def __init__(self, send, name):
   1219         self.__send = send
   1220         self.__name = name
   1221     def __getattr__(self, name):
   1222         return _Method(self.__send, "%s.%s" % (self.__name, name))
   1223     def __call__(self, *args):
   1224         return self.__send(self.__name, args)
   1225 
   1226 ##

   1227 # Standard transport class for XML-RPC over HTTP.

   1228 # <p>

   1229 # You can create custom transports by subclassing this method, and

   1230 # overriding selected methods.

   1231 
   1232 class Transport:
   1233     """Handles an HTTP transaction to an XML-RPC server."""
   1234 
   1235     # client identifier (may be overridden)

   1236     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
   1237 
   1238     #if true, we'll request gzip encoding

   1239     accept_gzip_encoding = True
   1240 
   1241     # if positive, encode request using gzip if it exceeds this threshold

   1242     # note that many server will get confused, so only use it if you know

   1243     # that they can decode such a request

   1244     encode_threshold = None #None = don't encode

   1245 
   1246     def __init__(self, use_datetime=0):
   1247         self._use_datetime = use_datetime
   1248         self._connection = (None, None)
   1249         self._extra_headers = []
   1250     ##

   1251     # Send a complete request, and parse the response.

   1252     # Retry request if a cached connection has disconnected.

   1253     #

   1254     # @param host Target host.

   1255     # @param handler Target PRC handler.

   1256     # @param request_body XML-RPC request body.

   1257     # @param verbose Debugging flag.

   1258     # @return Parsed response.

   1259 
   1260     def request(self, host, handler, request_body, verbose=0):
   1261         #retry request once if cached connection has gone cold

   1262         for i in (0, 1):
   1263             try:
   1264                 return self.single_request(host, handler, request_body, verbose)
   1265             except socket.error, e:
   1266                 if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
   1267                     raise
   1268             except httplib.BadStatusLine: #close after we sent request

   1269                 if i:
   1270                     raise
   1271 
   1272     ##

   1273     # Send a complete request, and parse the response.

   1274     #

   1275     # @param host Target host.

   1276     # @param handler Target PRC handler.

   1277     # @param request_body XML-RPC request body.

   1278     # @param verbose Debugging flag.

   1279     # @return Parsed response.

   1280 
   1281     def single_request(self, host, handler, request_body, verbose=0):
   1282         # issue XML-RPC request

   1283 
   1284         h = self.make_connection(host)
   1285         if verbose:
   1286             h.set_debuglevel(1)
   1287 
   1288         try:
   1289             self.send_request(h, handler, request_body)
   1290             self.send_host(h, host)
   1291             self.send_user_agent(h)
   1292             self.send_content(h, request_body)
   1293 
   1294             response = h.getresponse(buffering=True)
   1295             if response.status == 200:
   1296                 self.verbose = verbose
   1297                 return self.parse_response(response)
   1298         except Fault:
   1299             raise
   1300         except Exception:
   1301             # All unexpected errors leave connection in

   1302             # a strange state, so we clear it.

   1303             self.close()
   1304             raise
   1305 
   1306         #discard any response data and raise exception

   1307         if (response.getheader("content-length", 0)):
   1308             response.read()
   1309         raise ProtocolError(
   1310             host + handler,
   1311             response.status, response.reason,
   1312             response.msg,
   1313             )
   1314 
   1315     ##

   1316     # Create parser.

   1317     #

   1318     # @return A 2-tuple containing a parser and a unmarshaller.

   1319 
   1320     def getparser(self):
   1321         # get parser and unmarshaller

   1322         return getparser(use_datetime=self._use_datetime)
   1323 
   1324     ##

   1325     # Get authorization info from host parameter

   1326     # Host may be a string, or a (host, x509-dict) tuple; if a string,

   1327     # it is checked for a "user:pw@host" format, and a "Basic

   1328     # Authentication" header is added if appropriate.

   1329     #

   1330     # @param host Host descriptor (URL or (URL, x509 info) tuple).

   1331     # @return A 3-tuple containing (actual host, extra headers,

   1332     #     x509 info).  The header and x509 fields may be None.

   1333 
   1334     def get_host_info(self, host):
   1335 
   1336         x509 = {}
   1337         if isinstance(host, TupleType):
   1338             host, x509 = host
   1339 
   1340         import urllib
   1341         auth, host = urllib.splituser(host)
   1342 
   1343         if auth:
   1344             import base64
   1345             auth = base64.encodestring(urllib.unquote(auth))
   1346             auth = string.join(string.split(auth), "") # get rid of whitespace

   1347             extra_headers = [
   1348                 ("Authorization", "Basic " + auth)
   1349                 ]
   1350         else:
   1351             extra_headers = None
   1352 
   1353         return host, extra_headers, x509
   1354 
   1355     ##

   1356     # Connect to server.

   1357     #

   1358     # @param host Target host.

   1359     # @return A connection handle.

   1360 
   1361     def make_connection(self, host):
   1362         #return an existing connection if possible.  This allows

   1363         #HTTP/1.1 keep-alive.

   1364         if self._connection and host == self._connection[0]:
   1365             return self._connection[1]
   1366 
   1367         # create a HTTP connection object from a host descriptor

   1368         chost, self._extra_headers, x509 = self.get_host_info(host)
   1369         #store the host argument along with the connection object

   1370         self._connection = host, httplib.HTTPConnection(chost)
   1371         return self._connection[1]
   1372 
   1373     ##

   1374     # Clear any cached connection object.

   1375     # Used in the event of socket errors.

   1376     #

   1377     def close(self):
   1378         if self._connection[1]:
   1379             self._connection[1].close()
   1380             self._connection = (None, None)
   1381 
   1382     ##

   1383     # Send request header.

   1384     #

   1385     # @param connection Connection handle.

   1386     # @param handler Target RPC handler.

   1387     # @param request_body XML-RPC body.

   1388 
   1389     def send_request(self, connection, handler, request_body):
   1390         if (self.accept_gzip_encoding and gzip):
   1391             connection.putrequest("POST", handler, skip_accept_encoding=True)
   1392             connection.putheader("Accept-Encoding", "gzip")
   1393         else:
   1394             connection.putrequest("POST", handler)
   1395 
   1396     ##

   1397     # Send host name.

   1398     #

   1399     # @param connection Connection handle.

   1400     # @param host Host name.

   1401     #

   1402     # Note: This function doesn't actually add the "Host"

   1403     # header anymore, it is done as part of the connection.putrequest() in

   1404     # send_request() above.

   1405 
   1406     def send_host(self, connection, host):
   1407         extra_headers = self._extra_headers
   1408         if extra_headers:
   1409             if isinstance(extra_headers, DictType):
   1410                 extra_headers = extra_headers.items()
   1411             for key, value in extra_headers:
   1412                 connection.putheader(key, value)
   1413 
   1414     ##

   1415     # Send user-agent identifier.

   1416     #

   1417     # @param connection Connection handle.

   1418 
   1419     def send_user_agent(self, connection):
   1420         connection.putheader("User-Agent", self.user_agent)
   1421 
   1422     ##

   1423     # Send request body.

   1424     #

   1425     # @param connection Connection handle.

   1426     # @param request_body XML-RPC request body.

   1427 
   1428     def send_content(self, connection, request_body):
   1429         connection.putheader("Content-Type", "text/xml")
   1430 
   1431         #optionally encode the request

   1432         if (self.encode_threshold is not None and
   1433             self.encode_threshold < len(request_body) and
   1434             gzip):
   1435             connection.putheader("Content-Encoding", "gzip")
   1436             request_body = gzip_encode(request_body)
   1437 
   1438         connection.putheader("Content-Length", str(len(request_body)))
   1439         connection.endheaders(request_body)
   1440 
   1441     ##

   1442     # Parse response.

   1443     #

   1444     # @param file Stream.

   1445     # @return Response tuple and target method.

   1446 
   1447     def parse_response(self, response):
   1448         # read response data from httpresponse, and parse it

   1449 
   1450         # Check for new http response object, else it is a file object

   1451         if hasattr(response,'getheader'):
   1452             if response.getheader("Content-Encoding", "") == "gzip":
   1453                 stream = GzipDecodedResponse(response)
   1454             else:
   1455                 stream = response
   1456         else:
   1457             stream = response
   1458 
   1459         p, u = self.getparser()
   1460 
   1461         while 1:
   1462             data = stream.read(1024)
   1463             if not data:
   1464                 break
   1465             if self.verbose:
   1466                 print "body:", repr(data)
   1467             p.feed(data)
   1468 
   1469         if stream is not response:
   1470             stream.close()
   1471         p.close()
   1472 
   1473         return u.close()
   1474 
   1475 ##

   1476 # Standard transport class for XML-RPC over HTTPS.

   1477 
   1478 class SafeTransport(Transport):
   1479     """Handles an HTTPS transaction to an XML-RPC server."""
   1480 
   1481     # FIXME: mostly untested

   1482 
   1483     def make_connection(self, host):
   1484         if self._connection and host == self._connection[0]:
   1485             return self._connection[1]
   1486         # create a HTTPS connection object from a host descriptor

   1487         # host may be a string, or a (host, x509-dict) tuple

   1488         try:
   1489             HTTPS = httplib.HTTPSConnection
   1490         except AttributeError:
   1491             raise NotImplementedError(
   1492                 "your version of httplib doesn't support HTTPS"
   1493                 )
   1494         else:
   1495             chost, self._extra_headers, x509 = self.get_host_info(host)
   1496             self._connection = host, HTTPS(chost, None, **(x509 or {}))
   1497             return self._connection[1]
   1498 
   1499 ##

   1500 # Standard server proxy.  This class establishes a virtual connection

   1501 # to an XML-RPC server.

   1502 # <p>

   1503 # This class is available as ServerProxy and Server.  New code should

   1504 # use ServerProxy, to avoid confusion.

   1505 #

   1506 # @def ServerProxy(uri, **options)

   1507 # @param uri The connection point on the server.

   1508 # @keyparam transport A transport factory, compatible with the

   1509 #    standard transport class.

   1510 # @keyparam encoding The default encoding used for 8-bit strings

   1511 #    (default is UTF-8).

   1512 # @keyparam verbose Use a true value to enable debugging output.

   1513 #    (printed to standard output).

   1514 # @see Transport

   1515 
   1516 class ServerProxy:
   1517     """uri [,options] -> a logical connection to an XML-RPC server
   1518 
   1519     uri is the connection point on the server, given as
   1520     scheme://host/target.
   1521 
   1522     The standard implementation always supports the "http" scheme.  If
   1523     SSL socket support is available (Python 2.0), it also supports
   1524     "https".
   1525 
   1526     If the target part and the slash preceding it are both omitted,
   1527     "/RPC2" is assumed.
   1528 
   1529     The following options can be given as keyword arguments:
   1530 
   1531         transport: a transport factory
   1532         encoding: the request encoding (default is UTF-8)
   1533 
   1534     All 8-bit strings passed to the server proxy are assumed to use
   1535     the given encoding.
   1536     """
   1537 
   1538     def __init__(self, uri, transport=None, encoding=None, verbose=0,
   1539                  allow_none=0, use_datetime=0):
   1540         # establish a "logical" server connection

   1541 
   1542         # get the url

   1543         import urllib
   1544         type, uri = urllib.splittype(uri)
   1545         if type not in ("http", "https"):
   1546             raise IOError, "unsupported XML-RPC protocol"
   1547         self.__host, self.__handler = urllib.splithost(uri)
   1548         if not self.__handler:
   1549             self.__handler = "/RPC2"
   1550 
   1551         if transport is None:
   1552             if type == "https":
   1553                 transport = SafeTransport(use_datetime=use_datetime)
   1554             else:
   1555                 transport = Transport(use_datetime=use_datetime)
   1556         self.__transport = transport
   1557 
   1558         self.__encoding = encoding
   1559         self.__verbose = verbose
   1560         self.__allow_none = allow_none
   1561 
   1562     def __close(self):
   1563         self.__transport.close()
   1564 
   1565     def __request(self, methodname, params):
   1566         # call a method on the remote server

   1567 
   1568         request = dumps(params, methodname, encoding=self.__encoding,
   1569                         allow_none=self.__allow_none)
   1570 
   1571         response = self.__transport.request(
   1572             self.__host,
   1573             self.__handler,
   1574             request,
   1575             verbose=self.__verbose
   1576             )
   1577 
   1578         if len(response) == 1:
   1579             response = response[0]
   1580 
   1581         return response
   1582 
   1583     def __repr__(self):
   1584         return (
   1585             "<ServerProxy for %s%s>" %
   1586             (self.__host, self.__handler)
   1587             )
   1588 
   1589     __str__ = __repr__
   1590 
   1591     def __getattr__(self, name):
   1592         # magic method dispatcher

   1593         return _Method(self.__request, name)
   1594 
   1595     # note: to call a remote object with an non-standard name, use

   1596     # result getattr(server, "strange-python-name")(args)

   1597 
   1598     def __call__(self, attr):
   1599         """A workaround to get special attributes on the ServerProxy
   1600            without interfering with the magic __getattr__
   1601         """
   1602         if attr == "close":
   1603             return self.__close
   1604         elif attr == "transport":
   1605             return self.__transport
   1606         raise AttributeError("Attribute %r not found" % (attr,))
   1607 
   1608 # compatibility

   1609 
   1610 Server = ServerProxy
   1611 
   1612 # --------------------------------------------------------------------

   1613 # test code

   1614 
   1615 if __name__ == "__main__":
   1616 
   1617     # simple test program (from the XML-RPC specification)

   1618 
   1619     # server = ServerProxy("http://localhost:8000") # local server

   1620     server = ServerProxy("http://time.xmlrpc.com/RPC2")
   1621 
   1622     print server
   1623 
   1624     try:
   1625         print server.currentTime.getCurrentTime()
   1626     except Error, v:
   1627         print "ERROR", v
   1628 
   1629     multi = MultiCall(server)
   1630     multi.currentTime.getCurrentTime()
   1631     multi.currentTime.getCurrentTime()
   1632     try:
   1633         for response in multi():
   1634             print response
   1635     except Error, v:
   1636         print "ERROR", v
   1637