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# 2014-12-02 ch/doko  Add workaround for gzip bomb vulnerability
53#
54# Copyright (c) 1999-2002 by Secret Labs AB.
55# Copyright (c) 1999-2002 by Fredrik Lundh.
56#
57# info@pythonware.com
58# http://www.pythonware.com
59#
60# --------------------------------------------------------------------
61# The XML-RPC client interface is
62#
63# Copyright (c) 1999-2002 by Secret Labs AB
64# Copyright (c) 1999-2002 by Fredrik Lundh
65#
66# By obtaining, using, and/or copying this software and/or its
67# associated documentation, you agree that you have read, understood,
68# and will comply with the following terms and conditions:
69#
70# Permission to use, copy, modify, and distribute this software and
71# its associated documentation for any purpose and without fee is
72# hereby granted, provided that the above copyright notice appears in
73# all copies, and that both that copyright notice and this permission
74# notice appear in supporting documentation, and that the name of
75# Secret Labs AB or the author not be used in advertising or publicity
76# pertaining to distribution of the software without specific, written
77# prior permission.
78#
79# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
80# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
81# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
82# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
83# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
84# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
85# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
86# OF THIS SOFTWARE.
87# --------------------------------------------------------------------
88
89#
90# things to look into some day:
91
92# TODO: sort out True/False/boolean issues for Python 2.3
93
94"""
95An XML-RPC client interface for Python.
96
97The marshalling and response parser code can also be used to
98implement XML-RPC servers.
99
100Exported exceptions:
101
102  Error          Base class for client errors
103  ProtocolError  Indicates an HTTP protocol error
104  ResponseError  Indicates a broken response package
105  Fault          Indicates an XML-RPC fault package
106
107Exported classes:
108
109  ServerProxy    Represents a logical connection to an XML-RPC server
110
111  MultiCall      Executor of boxcared xmlrpc requests
112  Boolean        boolean wrapper to generate a "boolean" XML-RPC value
113  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
114                 localtime integer value to generate a "dateTime.iso8601"
115                 XML-RPC value
116  Binary         binary data wrapper
117
118  SlowParser     Slow but safe standard parser (based on xmllib)
119  Marshaller     Generate an XML-RPC params chunk from a Python data structure
120  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
121  Transport      Handles an HTTP transaction to an XML-RPC server
122  SafeTransport  Handles an HTTPS transaction to an XML-RPC server
123
124Exported constants:
125
126  True
127  False
128
129Exported functions:
130
131  boolean        Convert any Python value to an XML-RPC boolean
132  getparser      Create instance of the fastest available parser & attach
133                 to an unmarshalling object
134  dumps          Convert an argument tuple or a Fault instance to an XML-RPC
135                 request (or response, if the methodresponse option is used).
136  loads          Convert an XML-RPC packet to unmarshalled data plus a method
137                 name (None if not present).
138"""
139
140import re, string, time, operator
141
142from types import *
143import socket
144import errno
145import httplib
146try:
147    import gzip
148except ImportError:
149    gzip = None #python can be built without zlib/gzip support
150
151# --------------------------------------------------------------------
152# Internal stuff
153
154try:
155    unicode
156except NameError:
157    unicode = None # unicode support not available
158
159try:
160    import datetime
161except ImportError:
162    datetime = None
163
164try:
165    _bool_is_builtin = False.__class__.__name__ == "bool"
166except NameError:
167    _bool_is_builtin = 0
168
169def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
170    # decode non-ascii string (if possible)
171    if unicode and encoding and is8bit(data):
172        data = unicode(data, encoding)
173    return data
174
175def escape(s, replace=string.replace):
176    s = replace(s, "&", "&")
177    s = replace(s, "<", "&lt;")
178    return replace(s, ">", "&gt;",)
179
180if unicode:
181    def _stringify(string):
182        # convert to 7-bit ascii if possible
183        try:
184            return string.encode("ascii")
185        except UnicodeError:
186            return string
187else:
188    def _stringify(string):
189        return string
190
191__version__ = "1.0.1"
192
193# xmlrpc integer limits
194MAXINT =  2L**31-1
195MININT = -2L**31
196
197# --------------------------------------------------------------------
198# Error constants (from Dan Libby's specification at
199# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
200
201# Ranges of errors
202PARSE_ERROR       = -32700
203SERVER_ERROR      = -32600
204APPLICATION_ERROR = -32500
205SYSTEM_ERROR      = -32400
206TRANSPORT_ERROR   = -32300
207
208# Specific errors
209NOT_WELLFORMED_ERROR  = -32700
210UNSUPPORTED_ENCODING  = -32701
211INVALID_ENCODING_CHAR = -32702
212INVALID_XMLRPC        = -32600
213METHOD_NOT_FOUND      = -32601
214INVALID_METHOD_PARAMS = -32602
215INTERNAL_ERROR        = -32603
216
217# --------------------------------------------------------------------
218# Exceptions
219
220##
221# Base class for all kinds of client-side errors.
222
223class Error(Exception):
224    """Base class for client errors."""
225    def __str__(self):
226        return repr(self)
227
228##
229# Indicates an HTTP-level protocol error.  This is raised by the HTTP
230# transport layer, if the server returns an error code other than 200
231# (OK).
232#
233# @param url The target URL.
234# @param errcode The HTTP error code.
235# @param errmsg The HTTP error message.
236# @param headers The HTTP header dictionary.
237
238class ProtocolError(Error):
239    """Indicates an HTTP protocol error."""
240    def __init__(self, url, errcode, errmsg, headers):
241        Error.__init__(self)
242        self.url = url
243        self.errcode = errcode
244        self.errmsg = errmsg
245        self.headers = headers
246    def __repr__(self):
247        return (
248            "<ProtocolError for %s: %s %s>" %
249            (self.url, self.errcode, self.errmsg)
250            )
251
252##
253# Indicates a broken XML-RPC response package.  This exception is
254# raised by the unmarshalling layer, if the XML-RPC response is
255# malformed.
256
257class ResponseError(Error):
258    """Indicates a broken response package."""
259    pass
260
261##
262# Indicates an XML-RPC fault response package.  This exception is
263# raised by the unmarshalling layer, if the XML-RPC response contains
264# a fault string.  This exception can also used as a class, to
265# generate a fault XML-RPC message.
266#
267# @param faultCode The XML-RPC fault code.
268# @param faultString The XML-RPC fault string.
269
270class Fault(Error):
271    """Indicates an XML-RPC fault package."""
272    def __init__(self, faultCode, faultString, **extra):
273        Error.__init__(self)
274        self.faultCode = faultCode
275        self.faultString = faultString
276    def __repr__(self):
277        return (
278            "<Fault %s: %s>" %
279            (self.faultCode, repr(self.faultString))
280            )
281
282# --------------------------------------------------------------------
283# Special values
284
285##
286# Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
287# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
288# generate boolean XML-RPC values.
289#
290# @param value A boolean value.  Any true value is interpreted as True,
291#              all other values are interpreted as False.
292
293from sys import modules
294mod_dict = modules[__name__].__dict__
295if _bool_is_builtin:
296    boolean = Boolean = bool
297    # to avoid breaking code which references xmlrpclib.{True,False}
298    mod_dict['True'] = True
299    mod_dict['False'] = False
300else:
301    class Boolean:
302        """Boolean-value wrapper.
303
304        Use True or False to generate a "boolean" XML-RPC value.
305        """
306
307        def __init__(self, value = 0):
308            self.value = operator.truth(value)
309
310        def encode(self, out):
311            out.write("<value><boolean>%d</boolean></value>\n" % self.value)
312
313        def __cmp__(self, other):
314            if isinstance(other, Boolean):
315                other = other.value
316            return cmp(self.value, other)
317
318        def __repr__(self):
319            if self.value:
320                return "<Boolean True at %x>" % id(self)
321            else:
322                return "<Boolean False at %x>" % id(self)
323
324        def __int__(self):
325            return self.value
326
327        def __nonzero__(self):
328            return self.value
329
330    mod_dict['True'] = Boolean(1)
331    mod_dict['False'] = Boolean(0)
332
333    ##
334    # Map true or false value to XML-RPC boolean values.
335    #
336    # @def boolean(value)
337    # @param value A boolean value.  Any true value is mapped to True,
338    #              all other values are mapped to False.
339    # @return xmlrpclib.True or xmlrpclib.False.
340    # @see Boolean
341    # @see True
342    # @see False
343
344    def boolean(value, _truefalse=(False, True)):
345        """Convert any Python value to XML-RPC 'boolean'."""
346        return _truefalse[operator.truth(value)]
347
348del modules, mod_dict
349
350##
351# Wrapper for XML-RPC DateTime values.  This converts a time value to
352# the format used by XML-RPC.
353# <p>
354# The value can be given as a string in the format
355# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
356# time.localtime()), or an integer value (as returned by time.time()).
357# The wrapper uses time.localtime() to convert an integer to a time
358# tuple.
359#
360# @param value The time, given as an ISO 8601 string, a time
361#              tuple, or an integer time value.
362
363def _strftime(value):
364    if datetime:
365        if isinstance(value, datetime.datetime):
366            return "%04d%02d%02dT%02d:%02d:%02d" % (
367                value.year, value.month, value.day,
368                value.hour, value.minute, value.second)
369
370    if not isinstance(value, (TupleType, time.struct_time)):
371        if value == 0:
372            value = time.time()
373        value = time.localtime(value)
374
375    return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
376
377class DateTime:
378    """DateTime wrapper for an ISO 8601 string or time tuple or
379    localtime integer value to generate 'dateTime.iso8601' XML-RPC
380    value.
381    """
382
383    def __init__(self, value=0):
384        if isinstance(value, StringType):
385            self.value = value
386        else:
387            self.value = _strftime(value)
388
389    def make_comparable(self, other):
390        if isinstance(other, DateTime):
391            s = self.value
392            o = other.value
393        elif datetime and isinstance(other, datetime.datetime):
394            s = self.value
395            o = other.strftime("%Y%m%dT%H:%M:%S")
396        elif isinstance(other, basestring):
397            s = self.value
398            o = other
399        elif hasattr(other, "timetuple"):
400            s = self.timetuple()
401            o = other.timetuple()
402        else:
403            otype = (hasattr(other, "__class__")
404                     and other.__class__.__name__
405                     or type(other))
406            raise TypeError("Can't compare %s and %s" %
407                            (self.__class__.__name__, otype))
408        return s, o
409
410    def __lt__(self, other):
411        s, o = self.make_comparable(other)
412        return s < o
413
414    def __le__(self, other):
415        s, o = self.make_comparable(other)
416        return s <= o
417
418    def __gt__(self, other):
419        s, o = self.make_comparable(other)
420        return s > o
421
422    def __ge__(self, other):
423        s, o = self.make_comparable(other)
424        return s >= o
425
426    def __eq__(self, other):
427        s, o = self.make_comparable(other)
428        return s == o
429
430    def __ne__(self, other):
431        s, o = self.make_comparable(other)
432        return s != o
433
434    def timetuple(self):
435        return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
436
437    def __cmp__(self, other):
438        s, o = self.make_comparable(other)
439        return cmp(s, o)
440
441    ##
442    # Get date/time value.
443    #
444    # @return Date/time value, as an ISO 8601 string.
445
446    def __str__(self):
447        return self.value
448
449    def __repr__(self):
450        return "<DateTime %s at %x>" % (repr(self.value), id(self))
451
452    def decode(self, data):
453        data = str(data)
454        self.value = string.strip(data)
455
456    def encode(self, out):
457        out.write("<value><dateTime.iso8601>")
458        out.write(self.value)
459        out.write("</dateTime.iso8601></value>\n")
460
461def _datetime(data):
462    # decode xml element contents into a DateTime structure.
463    value = DateTime()
464    value.decode(data)
465    return value
466
467def _datetime_type(data):
468    t = time.strptime(data, "%Y%m%dT%H:%M:%S")
469    return datetime.datetime(*tuple(t)[:6])
470
471##
472# Wrapper for binary data.  This can be used to transport any kind
473# of binary data over XML-RPC, using BASE64 encoding.
474#
475# @param data An 8-bit string containing arbitrary data.
476
477import base64
478try:
479    import cStringIO as StringIO
480except ImportError:
481    import StringIO
482
483class Binary:
484    """Wrapper for binary data."""
485
486    def __init__(self, data=None):
487        self.data = data
488
489    ##
490    # Get buffer contents.
491    #
492    # @return Buffer contents, as an 8-bit string.
493
494    def __str__(self):
495        return self.data or ""
496
497    def __cmp__(self, other):
498        if isinstance(other, Binary):
499            other = other.data
500        return cmp(self.data, other)
501
502    def decode(self, data):
503        self.data = base64.decodestring(data)
504
505    def encode(self, out):
506        out.write("<value><base64>\n")
507        base64.encode(StringIO.StringIO(self.data), out)
508        out.write("</base64></value>\n")
509
510def _binary(data):
511    # decode xml element contents into a Binary structure
512    value = Binary()
513    value.decode(data)
514    return value
515
516WRAPPERS = (DateTime, Binary)
517if not _bool_is_builtin:
518    WRAPPERS = WRAPPERS + (Boolean,)
519
520# --------------------------------------------------------------------
521# XML parsers
522
523try:
524    # optional xmlrpclib accelerator
525    import _xmlrpclib
526    FastParser = _xmlrpclib.Parser
527    FastUnmarshaller = _xmlrpclib.Unmarshaller
528except (AttributeError, ImportError):
529    FastParser = FastUnmarshaller = None
530
531try:
532    import _xmlrpclib
533    FastMarshaller = _xmlrpclib.Marshaller
534except (AttributeError, ImportError):
535    FastMarshaller = None
536
537try:
538    from xml.parsers import expat
539    if not hasattr(expat, "ParserCreate"):
540        raise ImportError
541except ImportError:
542    ExpatParser = None # expat not available
543else:
544    class ExpatParser:
545        # fast expat parser for Python 2.0 and later.
546        def __init__(self, target):
547            self._parser = parser = expat.ParserCreate(None, None)
548            self._target = target
549            parser.StartElementHandler = target.start
550            parser.EndElementHandler = target.end
551            parser.CharacterDataHandler = target.data
552            encoding = None
553            if not parser.returns_unicode:
554                encoding = "utf-8"
555            target.xml(encoding, None)
556
557        def feed(self, data):
558            self._parser.Parse(data, 0)
559
560        def close(self):
561            try:
562                parser = self._parser
563            except AttributeError:
564                pass
565            else:
566                del self._target, self._parser # get rid of circular references
567                parser.Parse("", 1) # end of data
568
569class SlowParser:
570    """Default XML parser (based on xmllib.XMLParser)."""
571    # this is the slowest parser.
572    def __init__(self, target):
573        import xmllib # lazy subclassing (!)
574        if xmllib.XMLParser not in SlowParser.__bases__:
575            SlowParser.__bases__ = (xmllib.XMLParser,)
576        self.handle_xml = target.xml
577        self.unknown_starttag = target.start
578        self.handle_data = target.data
579        self.handle_cdata = target.data
580        self.unknown_endtag = target.end
581        try:
582            xmllib.XMLParser.__init__(self, accept_utf8=1)
583        except TypeError:
584            xmllib.XMLParser.__init__(self) # pre-2.0
585
586# --------------------------------------------------------------------
587# XML-RPC marshalling and unmarshalling code
588
589##
590# XML-RPC marshaller.
591#
592# @param encoding Default encoding for 8-bit strings.  The default
593#     value is None (interpreted as UTF-8).
594# @see dumps
595
596class Marshaller:
597    """Generate an XML-RPC params chunk from a Python data structure.
598
599    Create a Marshaller instance for each set of parameters, and use
600    the "dumps" method to convert your data (represented as a tuple)
601    to an XML-RPC params chunk.  To write a fault response, pass a
602    Fault instance instead.  You may prefer to use the "dumps" module
603    function for this purpose.
604    """
605
606    # by the way, if you don't understand what's going on in here,
607    # that's perfectly ok.
608
609    def __init__(self, encoding=None, allow_none=0):
610        self.memo = {}
611        self.data = None
612        self.encoding = encoding
613        self.allow_none = allow_none
614
615    dispatch = {}
616
617    def dumps(self, values):
618        out = []
619        write = out.append
620        dump = self.__dump
621        if isinstance(values, Fault):
622            # fault instance
623            write("<fault>\n")
624            dump({'faultCode': values.faultCode,
625                  'faultString': values.faultString},
626                 write)
627            write("</fault>\n")
628        else:
629            # parameter block
630            # FIXME: the xml-rpc specification allows us to leave out
631            # the entire <params> block if there are no parameters.
632            # however, changing this may break older code (including
633            # old versions of xmlrpclib.py), so this is better left as
634            # is for now.  See @XMLRPC3 for more information. /F
635            write("<params>\n")
636            for v in values:
637                write("<param>\n")
638                dump(v, write)
639                write("</param>\n")
640            write("</params>\n")
641        result = string.join(out, "")
642        return result
643
644    def __dump(self, value, write):
645        try:
646            f = self.dispatch[type(value)]
647        except KeyError:
648            # check if this object can be marshalled as a structure
649            try:
650                value.__dict__
651            except:
652                raise TypeError, "cannot marshal %s objects" % type(value)
653            # check if this class is a sub-class of a basic type,
654            # because we don't know how to marshal these types
655            # (e.g. a string sub-class)
656            for type_ in type(value).__mro__:
657                if type_ in self.dispatch.keys():
658                    raise TypeError, "cannot marshal %s objects" % type(value)
659            f = self.dispatch[InstanceType]
660        f(self, value, write)
661
662    def dump_nil (self, value, write):
663        if not self.allow_none:
664            raise TypeError, "cannot marshal None unless allow_none is enabled"
665        write("<value><nil/></value>")
666    dispatch[NoneType] = dump_nil
667
668    def dump_int(self, value, write):
669        # in case ints are > 32 bits
670        if value > MAXINT or value < MININT:
671            raise OverflowError, "int exceeds XML-RPC limits"
672        write("<value><int>")
673        write(str(value))
674        write("</int></value>\n")
675    dispatch[IntType] = dump_int
676
677    if _bool_is_builtin:
678        def dump_bool(self, value, write):
679            write("<value><boolean>")
680            write(value and "1" or "0")
681            write("</boolean></value>\n")
682        dispatch[bool] = dump_bool
683
684    def dump_long(self, value, write):
685        if value > MAXINT or value < MININT:
686            raise OverflowError, "long int exceeds XML-RPC limits"
687        write("<value><int>")
688        write(str(int(value)))
689        write("</int></value>\n")
690    dispatch[LongType] = dump_long
691
692    def dump_double(self, value, write):
693        write("<value><double>")
694        write(repr(value))
695        write("</double></value>\n")
696    dispatch[FloatType] = dump_double
697
698    def dump_string(self, value, write, escape=escape):
699        write("<value><string>")
700        write(escape(value))
701        write("</string></value>\n")
702    dispatch[StringType] = dump_string
703
704    if unicode:
705        def dump_unicode(self, value, write, escape=escape):
706            write("<value><string>")
707            write(escape(value).encode(self.encoding, 'xmlcharrefreplace'))
708            write("</string></value>\n")
709        dispatch[UnicodeType] = dump_unicode
710
711    def dump_array(self, value, write):
712        i = id(value)
713        if i in self.memo:
714            raise TypeError, "cannot marshal recursive sequences"
715        self.memo[i] = None
716        dump = self.__dump
717        write("<value><array><data>\n")
718        for v in value:
719            dump(v, write)
720        write("</data></array></value>\n")
721        del self.memo[i]
722    dispatch[TupleType] = dump_array
723    dispatch[ListType] = dump_array
724
725    def dump_struct(self, value, write, escape=escape):
726        i = id(value)
727        if i in self.memo:
728            raise TypeError, "cannot marshal recursive dictionaries"
729        self.memo[i] = None
730        dump = self.__dump
731        write("<value><struct>\n")
732        for k, v in value.items():
733            write("<member>\n")
734            if type(k) is StringType:
735                k = escape(k)
736            elif unicode and type(k) is UnicodeType:
737                k = escape(k).encode(self.encoding, 'xmlcharrefreplace')
738            else:
739                raise TypeError, "dictionary key must be string"
740            write("<name>%s</name>\n" % k)
741            dump(v, write)
742            write("</member>\n")
743        write("</struct></value>\n")
744        del self.memo[i]
745    dispatch[DictType] = dump_struct
746
747    if datetime:
748        def dump_datetime(self, value, write):
749            write("<value><dateTime.iso8601>")
750            write(_strftime(value))
751            write("</dateTime.iso8601></value>\n")
752        dispatch[datetime.datetime] = dump_datetime
753
754    def dump_instance(self, value, write):
755        # check for special wrappers
756        if value.__class__ in WRAPPERS:
757            self.write = write
758            value.encode(self)
759            del self.write
760        else:
761            # store instance attributes as a struct (really?)
762            self.dump_struct(value.__dict__, write)
763    dispatch[InstanceType] = dump_instance
764
765##
766# XML-RPC unmarshaller.
767#
768# @see loads
769
770class Unmarshaller:
771    """Unmarshal an XML-RPC response, based on incoming XML event
772    messages (start, data, end).  Call close() to get the resulting
773    data structure.
774
775    Note that this reader is fairly tolerant, and gladly accepts bogus
776    XML-RPC data without complaining (but not bogus XML).
777    """
778
779    # and again, if you don't understand what's going on in here,
780    # that's perfectly ok.
781
782    def __init__(self, use_datetime=0):
783        self._type = None
784        self._stack = []
785        self._marks = []
786        self._data = []
787        self._value = False
788        self._methodname = None
789        self._encoding = "utf-8"
790        self.append = self._stack.append
791        self._use_datetime = use_datetime
792        if use_datetime and not datetime:
793            raise ValueError, "the datetime module is not available"
794
795    def close(self):
796        # return response tuple and target method
797        if self._type is None or self._marks:
798            raise ResponseError()
799        if self._type == "fault":
800            raise Fault(**self._stack[0])
801        return tuple(self._stack)
802
803    def getmethodname(self):
804        return self._methodname
805
806    #
807    # event handlers
808
809    def xml(self, encoding, standalone):
810        self._encoding = encoding
811        # FIXME: assert standalone == 1 ???
812
813    def start(self, tag, attrs):
814        # prepare to handle this element
815        if tag == "array" or tag == "struct":
816            self._marks.append(len(self._stack))
817        self._data = []
818        if self._value and tag not in self.dispatch:
819            raise ResponseError("unknown tag %r" % tag)
820        self._value = (tag == "value")
821
822    def data(self, text):
823        self._data.append(text)
824
825    def end(self, tag, join=string.join):
826        # call the appropriate end tag handler
827        try:
828            f = self.dispatch[tag]
829        except KeyError:
830            pass # unknown tag ?
831        else:
832            return f(self, join(self._data, ""))
833
834    #
835    # accelerator support
836
837    def end_dispatch(self, tag, data):
838        # dispatch data
839        try:
840            f = self.dispatch[tag]
841        except KeyError:
842            pass # unknown tag ?
843        else:
844            return f(self, data)
845
846    #
847    # element decoders
848
849    dispatch = {}
850
851    def end_nil (self, data):
852        self.append(None)
853        self._value = 0
854    dispatch["nil"] = end_nil
855
856    def end_boolean(self, data):
857        if data == "0":
858            self.append(False)
859        elif data == "1":
860            self.append(True)
861        else:
862            raise TypeError, "bad boolean value"
863        self._value = 0
864    dispatch["boolean"] = end_boolean
865
866    def end_int(self, data):
867        self.append(int(data))
868        self._value = 0
869    dispatch["i4"] = end_int
870    dispatch["i8"] = end_int
871    dispatch["int"] = end_int
872
873    def end_double(self, data):
874        self.append(float(data))
875        self._value = 0
876    dispatch["double"] = end_double
877
878    def end_string(self, data):
879        if self._encoding:
880            data = _decode(data, self._encoding)
881        self.append(_stringify(data))
882        self._value = 0
883    dispatch["string"] = end_string
884    dispatch["name"] = end_string # struct keys are always strings
885
886    def end_array(self, data):
887        mark = self._marks.pop()
888        # map arrays to Python lists
889        self._stack[mark:] = [self._stack[mark:]]
890        self._value = 0
891    dispatch["array"] = end_array
892
893    def end_struct(self, data):
894        mark = self._marks.pop()
895        # map structs to Python dictionaries
896        dict = {}
897        items = self._stack[mark:]
898        for i in range(0, len(items), 2):
899            dict[_stringify(items[i])] = items[i+1]
900        self._stack[mark:] = [dict]
901        self._value = 0
902    dispatch["struct"] = end_struct
903
904    def end_base64(self, data):
905        value = Binary()
906        value.decode(data)
907        self.append(value)
908        self._value = 0
909    dispatch["base64"] = end_base64
910
911    def end_dateTime(self, data):
912        value = DateTime()
913        value.decode(data)
914        if self._use_datetime:
915            value = _datetime_type(data)
916        self.append(value)
917    dispatch["dateTime.iso8601"] = end_dateTime
918
919    def end_value(self, data):
920        # if we stumble upon a value element with no internal
921        # elements, treat it as a string element
922        if self._value:
923            self.end_string(data)
924    dispatch["value"] = end_value
925
926    def end_params(self, data):
927        self._type = "params"
928    dispatch["params"] = end_params
929
930    def end_fault(self, data):
931        self._type = "fault"
932    dispatch["fault"] = end_fault
933
934    def end_methodName(self, data):
935        if self._encoding:
936            data = _decode(data, self._encoding)
937        self._methodname = data
938        self._type = "methodName" # no params
939    dispatch["methodName"] = end_methodName
940
941## Multicall support
942#
943
944class _MultiCallMethod:
945    # some lesser magic to store calls made to a MultiCall object
946    # for batch execution
947    def __init__(self, call_list, name):
948        self.__call_list = call_list
949        self.__name = name
950    def __getattr__(self, name):
951        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
952    def __call__(self, *args):
953        self.__call_list.append((self.__name, args))
954
955class MultiCallIterator:
956    """Iterates over the results of a multicall. Exceptions are
957    raised in response to xmlrpc faults."""
958
959    def __init__(self, results):
960        self.results = results
961
962    def __getitem__(self, i):
963        item = self.results[i]
964        if type(item) == type({}):
965            raise Fault(item['faultCode'], item['faultString'])
966        elif type(item) == type([]):
967            return item[0]
968        else:
969            raise ValueError,\
970                  "unexpected type in multicall result"
971
972class MultiCall:
973    """server -> an object used to boxcar method calls
974
975    server should be a ServerProxy object.
976
977    Methods can be added to the MultiCall using normal
978    method call syntax e.g.:
979
980    multicall = MultiCall(server_proxy)
981    multicall.add(2,3)
982    multicall.get_address("Guido")
983
984    To execute the multicall, call the MultiCall object e.g.:
985
986    add_result, address = multicall()
987    """
988
989    def __init__(self, server):
990        self.__server = server
991        self.__call_list = []
992
993    def __repr__(self):
994        return "<MultiCall at %x>" % id(self)
995
996    __str__ = __repr__
997
998    def __getattr__(self, name):
999        return _MultiCallMethod(self.__call_list, name)
1000
1001    def __call__(self):
1002        marshalled_list = []
1003        for name, args in self.__call_list:
1004            marshalled_list.append({'methodName' : name, 'params' : args})
1005
1006        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
1007
1008# --------------------------------------------------------------------
1009# convenience functions
1010
1011##
1012# Create a parser object, and connect it to an unmarshalling instance.
1013# This function picks the fastest available XML parser.
1014#
1015# return A (parser, unmarshaller) tuple.
1016
1017def getparser(use_datetime=0):
1018    """getparser() -> parser, unmarshaller
1019
1020    Create an instance of the fastest available parser, and attach it
1021    to an unmarshalling object.  Return both objects.
1022    """
1023    if use_datetime and not datetime:
1024        raise ValueError, "the datetime module is not available"
1025    if FastParser and FastUnmarshaller:
1026        if use_datetime:
1027            mkdatetime = _datetime_type
1028        else:
1029            mkdatetime = _datetime
1030        target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
1031        parser = FastParser(target)
1032    else:
1033        target = Unmarshaller(use_datetime=use_datetime)
1034        if FastParser:
1035            parser = FastParser(target)
1036        elif ExpatParser:
1037            parser = ExpatParser(target)
1038        else:
1039            parser = SlowParser(target)
1040    return parser, target
1041
1042##
1043# Convert a Python tuple or a Fault instance to an XML-RPC packet.
1044#
1045# @def dumps(params, **options)
1046# @param params A tuple or Fault instance.
1047# @keyparam methodname If given, create a methodCall request for
1048#     this method name.
1049# @keyparam methodresponse If given, create a methodResponse packet.
1050#     If used with a tuple, the tuple must be a singleton (that is,
1051#     it must contain exactly one element).
1052# @keyparam encoding The packet encoding.
1053# @return A string containing marshalled data.
1054
1055def dumps(params, methodname=None, methodresponse=None, encoding=None,
1056          allow_none=0):
1057    """data [,options] -> marshalled data
1058
1059    Convert an argument tuple or a Fault instance to an XML-RPC
1060    request (or response, if the methodresponse option is used).
1061
1062    In addition to the data object, the following options can be given
1063    as keyword arguments:
1064
1065        methodname: the method name for a methodCall packet
1066
1067        methodresponse: true to create a methodResponse packet.
1068        If this option is used with a tuple, the tuple must be
1069        a singleton (i.e. it can contain only one element).
1070
1071        encoding: the packet encoding (default is UTF-8)
1072
1073    All 8-bit strings in the data structure are assumed to use the
1074    packet encoding.  Unicode strings are automatically converted,
1075    where necessary.
1076    """
1077
1078    assert isinstance(params, TupleType) or isinstance(params, Fault),\
1079           "argument must be tuple or Fault instance"
1080
1081    if isinstance(params, Fault):
1082        methodresponse = 1
1083    elif methodresponse and isinstance(params, TupleType):
1084        assert len(params) == 1, "response tuple must be a singleton"
1085
1086    if not encoding:
1087        encoding = "utf-8"
1088
1089    if FastMarshaller:
1090        m = FastMarshaller(encoding)
1091    else:
1092        m = Marshaller(encoding, allow_none)
1093
1094    data = m.dumps(params)
1095
1096    if encoding != "utf-8":
1097        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
1098    else:
1099        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
1100
1101    # standard XML-RPC wrappings
1102    if methodname:
1103        # a method call
1104        if not isinstance(methodname, StringType):
1105            methodname = methodname.encode(encoding, 'xmlcharrefreplace')
1106        data = (
1107            xmlheader,
1108            "<methodCall>\n"
1109            "<methodName>", methodname, "</methodName>\n",
1110            data,
1111            "</methodCall>\n"
1112            )
1113    elif methodresponse:
1114        # a method response, or a fault structure
1115        data = (
1116            xmlheader,
1117            "<methodResponse>\n",
1118            data,
1119            "</methodResponse>\n"
1120            )
1121    else:
1122        return data # return as is
1123    return string.join(data, "")
1124
1125##
1126# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
1127# represents a fault condition, this function raises a Fault exception.
1128#
1129# @param data An XML-RPC packet, given as an 8-bit string.
1130# @return A tuple containing the unpacked data, and the method name
1131#     (None if not present).
1132# @see Fault
1133
1134def loads(data, use_datetime=0):
1135    """data -> unmarshalled data, method name
1136
1137    Convert an XML-RPC packet to unmarshalled data plus a method
1138    name (None if not present).
1139
1140    If the XML-RPC packet represents a fault condition, this function
1141    raises a Fault exception.
1142    """
1143    p, u = getparser(use_datetime=use_datetime)
1144    p.feed(data)
1145    p.close()
1146    return u.close(), u.getmethodname()
1147
1148##
1149# Encode a string using the gzip content encoding such as specified by the
1150# Content-Encoding: gzip
1151# in the HTTP header, as described in RFC 1952
1152#
1153# @param data the unencoded data
1154# @return the encoded data
1155
1156def gzip_encode(data):
1157    """data -> gzip encoded data
1158
1159    Encode data using the gzip content encoding as described in RFC 1952
1160    """
1161    if not gzip:
1162        raise NotImplementedError
1163    f = StringIO.StringIO()
1164    gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
1165    gzf.write(data)
1166    gzf.close()
1167    encoded = f.getvalue()
1168    f.close()
1169    return encoded
1170
1171##
1172# Decode a string using the gzip content encoding such as specified by the
1173# Content-Encoding: gzip
1174# in the HTTP header, as described in RFC 1952
1175#
1176# @param data The encoded data
1177# @keyparam max_decode Maximum bytes to decode (20MB default), use negative
1178#    values for unlimited decoding
1179# @return the unencoded data
1180# @raises ValueError if data is not correctly coded.
1181# @raises ValueError if max gzipped payload length exceeded
1182
1183def gzip_decode(data, max_decode=20971520):
1184    """gzip encoded data -> unencoded data
1185
1186    Decode data using the gzip content encoding as described in RFC 1952
1187    """
1188    if not gzip:
1189        raise NotImplementedError
1190    f = StringIO.StringIO(data)
1191    gzf = gzip.GzipFile(mode="rb", fileobj=f)
1192    try:
1193        if max_decode < 0: # no limit
1194            decoded = gzf.read()
1195        else:
1196            decoded = gzf.read(max_decode + 1)
1197    except IOError:
1198        raise ValueError("invalid data")
1199    f.close()
1200    gzf.close()
1201    if max_decode >= 0 and len(decoded) > max_decode:
1202        raise ValueError("max gzipped payload length exceeded")
1203    return decoded
1204
1205##
1206# Return a decoded file-like object for the gzip encoding
1207# as described in RFC 1952.
1208#
1209# @param response A stream supporting a read() method
1210# @return a file-like object that the decoded data can be read() from
1211
1212class GzipDecodedResponse(gzip.GzipFile if gzip else object):
1213    """a file-like object to decode a response encoded with the gzip
1214    method, as described in RFC 1952.
1215    """
1216    def __init__(self, response):
1217        #response doesn't support tell() and read(), required by
1218        #GzipFile
1219        if not gzip:
1220            raise NotImplementedError
1221        self.stringio = StringIO.StringIO(response.read())
1222        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
1223
1224    def close(self):
1225        try:
1226            gzip.GzipFile.close(self)
1227        finally:
1228            self.stringio.close()
1229
1230
1231# --------------------------------------------------------------------
1232# request dispatcher
1233
1234class _Method:
1235    # some magic to bind an XML-RPC method to an RPC server.
1236    # supports "nested" methods (e.g. examples.getStateName)
1237    def __init__(self, send, name):
1238        self.__send = send
1239        self.__name = name
1240    def __getattr__(self, name):
1241        return _Method(self.__send, "%s.%s" % (self.__name, name))
1242    def __call__(self, *args):
1243        return self.__send(self.__name, args)
1244
1245##
1246# Standard transport class for XML-RPC over HTTP.
1247# <p>
1248# You can create custom transports by subclassing this method, and
1249# overriding selected methods.
1250
1251class Transport:
1252    """Handles an HTTP transaction to an XML-RPC server."""
1253
1254    # client identifier (may be overridden)
1255    user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1256
1257    #if true, we'll request gzip encoding
1258    accept_gzip_encoding = True
1259
1260    # if positive, encode request using gzip if it exceeds this threshold
1261    # note that many server will get confused, so only use it if you know
1262    # that they can decode such a request
1263    encode_threshold = None #None = don't encode
1264
1265    def __init__(self, use_datetime=0):
1266        self._use_datetime = use_datetime
1267        self._connection = (None, None)
1268        self._extra_headers = []
1269    ##
1270    # Send a complete request, and parse the response.
1271    # Retry request if a cached connection has disconnected.
1272    #
1273    # @param host Target host.
1274    # @param handler Target PRC handler.
1275    # @param request_body XML-RPC request body.
1276    # @param verbose Debugging flag.
1277    # @return Parsed response.
1278
1279    def request(self, host, handler, request_body, verbose=0):
1280        #retry request once if cached connection has gone cold
1281        for i in (0, 1):
1282            try:
1283                return self.single_request(host, handler, request_body, verbose)
1284            except socket.error, e:
1285                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
1286                    raise
1287            except httplib.BadStatusLine: #close after we sent request
1288                if i:
1289                    raise
1290
1291    ##
1292    # Send a complete request, and parse the response.
1293    #
1294    # @param host Target host.
1295    # @param handler Target PRC handler.
1296    # @param request_body XML-RPC request body.
1297    # @param verbose Debugging flag.
1298    # @return Parsed response.
1299
1300    def single_request(self, host, handler, request_body, verbose=0):
1301        # issue XML-RPC request
1302
1303        h = self.make_connection(host)
1304        if verbose:
1305            h.set_debuglevel(1)
1306
1307        try:
1308            self.send_request(h, handler, request_body)
1309            self.send_host(h, host)
1310            self.send_user_agent(h)
1311            self.send_content(h, request_body)
1312
1313            response = h.getresponse(buffering=True)
1314            if response.status == 200:
1315                self.verbose = verbose
1316                return self.parse_response(response)
1317        except Fault:
1318            raise
1319        except Exception:
1320            # All unexpected errors leave connection in
1321            # a strange state, so we clear it.
1322            self.close()
1323            raise
1324
1325        #discard any response data and raise exception
1326        if (response.getheader("content-length", 0)):
1327            response.read()
1328        raise ProtocolError(
1329            host + handler,
1330            response.status, response.reason,
1331            response.msg,
1332            )
1333
1334    ##
1335    # Create parser.
1336    #
1337    # @return A 2-tuple containing a parser and an unmarshaller.
1338
1339    def getparser(self):
1340        # get parser and unmarshaller
1341        return getparser(use_datetime=self._use_datetime)
1342
1343    ##
1344    # Get authorization info from host parameter
1345    # Host may be a string, or a (host, x509-dict) tuple; if a string,
1346    # it is checked for a "user:pw@host" format, and a "Basic
1347    # Authentication" header is added if appropriate.
1348    #
1349    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1350    # @return A 3-tuple containing (actual host, extra headers,
1351    #     x509 info).  The header and x509 fields may be None.
1352
1353    def get_host_info(self, host):
1354
1355        x509 = {}
1356        if isinstance(host, TupleType):
1357            host, x509 = host
1358
1359        import urllib
1360        auth, host = urllib.splituser(host)
1361
1362        if auth:
1363            import base64
1364            auth = base64.encodestring(urllib.unquote(auth))
1365            auth = string.join(string.split(auth), "") # get rid of whitespace
1366            extra_headers = [
1367                ("Authorization", "Basic " + auth)
1368                ]
1369        else:
1370            extra_headers = None
1371
1372        return host, extra_headers, x509
1373
1374    ##
1375    # Connect to server.
1376    #
1377    # @param host Target host.
1378    # @return A connection handle.
1379
1380    def make_connection(self, host):
1381        #return an existing connection if possible.  This allows
1382        #HTTP/1.1 keep-alive.
1383        if self._connection and host == self._connection[0]:
1384            return self._connection[1]
1385
1386        # create a HTTP connection object from a host descriptor
1387        chost, self._extra_headers, x509 = self.get_host_info(host)
1388        #store the host argument along with the connection object
1389        self._connection = host, httplib.HTTPConnection(chost)
1390        return self._connection[1]
1391
1392    ##
1393    # Clear any cached connection object.
1394    # Used in the event of socket errors.
1395    #
1396    def close(self):
1397        host, connection = self._connection
1398        if connection:
1399            self._connection = (None, None)
1400            connection.close()
1401
1402    ##
1403    # Send request header.
1404    #
1405    # @param connection Connection handle.
1406    # @param handler Target RPC handler.
1407    # @param request_body XML-RPC body.
1408
1409    def send_request(self, connection, handler, request_body):
1410        if (self.accept_gzip_encoding and gzip):
1411            connection.putrequest("POST", handler, skip_accept_encoding=True)
1412            connection.putheader("Accept-Encoding", "gzip")
1413        else:
1414            connection.putrequest("POST", handler)
1415
1416    ##
1417    # Send host name.
1418    #
1419    # @param connection Connection handle.
1420    # @param host Host name.
1421    #
1422    # Note: This function doesn't actually add the "Host"
1423    # header anymore, it is done as part of the connection.putrequest() in
1424    # send_request() above.
1425
1426    def send_host(self, connection, host):
1427        extra_headers = self._extra_headers
1428        if extra_headers:
1429            if isinstance(extra_headers, DictType):
1430                extra_headers = extra_headers.items()
1431            for key, value in extra_headers:
1432                connection.putheader(key, value)
1433
1434    ##
1435    # Send user-agent identifier.
1436    #
1437    # @param connection Connection handle.
1438
1439    def send_user_agent(self, connection):
1440        connection.putheader("User-Agent", self.user_agent)
1441
1442    ##
1443    # Send request body.
1444    #
1445    # @param connection Connection handle.
1446    # @param request_body XML-RPC request body.
1447
1448    def send_content(self, connection, request_body):
1449        connection.putheader("Content-Type", "text/xml")
1450
1451        #optionally encode the request
1452        if (self.encode_threshold is not None and
1453            self.encode_threshold < len(request_body) and
1454            gzip):
1455            connection.putheader("Content-Encoding", "gzip")
1456            request_body = gzip_encode(request_body)
1457
1458        connection.putheader("Content-Length", str(len(request_body)))
1459        connection.endheaders(request_body)
1460
1461    ##
1462    # Parse response.
1463    #
1464    # @param file Stream.
1465    # @return Response tuple and target method.
1466
1467    def parse_response(self, response):
1468        # read response data from httpresponse, and parse it
1469
1470        # Check for new http response object, else it is a file object
1471        if hasattr(response,'getheader'):
1472            if response.getheader("Content-Encoding", "") == "gzip":
1473                stream = GzipDecodedResponse(response)
1474            else:
1475                stream = response
1476        else:
1477            stream = response
1478
1479        p, u = self.getparser()
1480
1481        while 1:
1482            data = stream.read(1024)
1483            if not data:
1484                break
1485            if self.verbose:
1486                print "body:", repr(data)
1487            p.feed(data)
1488
1489        if stream is not response:
1490            stream.close()
1491        p.close()
1492
1493        return u.close()
1494
1495##
1496# Standard transport class for XML-RPC over HTTPS.
1497
1498class SafeTransport(Transport):
1499    """Handles an HTTPS transaction to an XML-RPC server."""
1500
1501    def __init__(self, use_datetime=0, context=None):
1502        Transport.__init__(self, use_datetime=use_datetime)
1503        self.context = context
1504
1505    # FIXME: mostly untested
1506
1507    def make_connection(self, host):
1508        if self._connection and host == self._connection[0]:
1509            return self._connection[1]
1510        # create a HTTPS connection object from a host descriptor
1511        # host may be a string, or a (host, x509-dict) tuple
1512        try:
1513            HTTPS = httplib.HTTPSConnection
1514        except AttributeError:
1515            raise NotImplementedError(
1516                "your version of httplib doesn't support HTTPS"
1517                )
1518        else:
1519            chost, self._extra_headers, x509 = self.get_host_info(host)
1520            self._connection = host, HTTPS(chost, None, context=self.context, **(x509 or {}))
1521            return self._connection[1]
1522
1523##
1524# Standard server proxy.  This class establishes a virtual connection
1525# to an XML-RPC server.
1526# <p>
1527# This class is available as ServerProxy and Server.  New code should
1528# use ServerProxy, to avoid confusion.
1529#
1530# @def ServerProxy(uri, **options)
1531# @param uri The connection point on the server.
1532# @keyparam transport A transport factory, compatible with the
1533#    standard transport class.
1534# @keyparam encoding The default encoding used for 8-bit strings
1535#    (default is UTF-8).
1536# @keyparam verbose Use a true value to enable debugging output.
1537#    (printed to standard output).
1538# @see Transport
1539
1540class ServerProxy:
1541    """uri [,options] -> a logical connection to an XML-RPC server
1542
1543    uri is the connection point on the server, given as
1544    scheme://host/target.
1545
1546    The standard implementation always supports the "http" scheme.  If
1547    SSL socket support is available (Python 2.0), it also supports
1548    "https".
1549
1550    If the target part and the slash preceding it are both omitted,
1551    "/RPC2" is assumed.
1552
1553    The following options can be given as keyword arguments:
1554
1555        transport: a transport factory
1556        encoding: the request encoding (default is UTF-8)
1557
1558    All 8-bit strings passed to the server proxy are assumed to use
1559    the given encoding.
1560    """
1561
1562    def __init__(self, uri, transport=None, encoding=None, verbose=0,
1563                 allow_none=0, use_datetime=0, context=None):
1564        # establish a "logical" server connection
1565
1566        if unicode and isinstance(uri, unicode):
1567            uri = uri.encode('ISO-8859-1')
1568
1569        # get the url
1570        import urllib
1571        type, uri = urllib.splittype(uri)
1572        if type not in ("http", "https"):
1573            raise IOError, "unsupported XML-RPC protocol"
1574        self.__host, self.__handler = urllib.splithost(uri)
1575        if not self.__handler:
1576            self.__handler = "/RPC2"
1577
1578        if transport is None:
1579            if type == "https":
1580                transport = SafeTransport(use_datetime=use_datetime, context=context)
1581            else:
1582                transport = Transport(use_datetime=use_datetime)
1583        self.__transport = transport
1584
1585        self.__encoding = encoding
1586        self.__verbose = verbose
1587        self.__allow_none = allow_none
1588
1589    def __close(self):
1590        self.__transport.close()
1591
1592    def __request(self, methodname, params):
1593        # call a method on the remote server
1594
1595        request = dumps(params, methodname, encoding=self.__encoding,
1596                        allow_none=self.__allow_none)
1597
1598        response = self.__transport.request(
1599            self.__host,
1600            self.__handler,
1601            request,
1602            verbose=self.__verbose
1603            )
1604
1605        if len(response) == 1:
1606            response = response[0]
1607
1608        return response
1609
1610    def __repr__(self):
1611        return (
1612            "<ServerProxy for %s%s>" %
1613            (self.__host, self.__handler)
1614            )
1615
1616    __str__ = __repr__
1617
1618    def __getattr__(self, name):
1619        # magic method dispatcher
1620        return _Method(self.__request, name)
1621
1622    # note: to call a remote object with a non-standard name, use
1623    # result getattr(server, "strange-python-name")(args)
1624
1625    def __call__(self, attr):
1626        """A workaround to get special attributes on the ServerProxy
1627           without interfering with the magic __getattr__
1628        """
1629        if attr == "close":
1630            return self.__close
1631        elif attr == "transport":
1632            return self.__transport
1633        raise AttributeError("Attribute %r not found" % (attr,))
1634
1635# compatibility
1636
1637Server = ServerProxy
1638
1639# --------------------------------------------------------------------
1640# test code
1641
1642if __name__ == "__main__":
1643
1644    server = ServerProxy("http://localhost:8000")
1645
1646    print server
1647
1648    multi = MultiCall(server)
1649    multi.pow(2, 9)
1650    multi.add(5, 1)
1651    multi.add(24, 11)
1652    try:
1653        for response in multi():
1654            print response
1655    except Error, v:
1656        print "ERROR", v
1657