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@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"""
94An XML-RPC client interface for Python.
95
96The marshalling and response parser code can also be used to
97implement XML-RPC servers.
98
99Exported 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
106Exported 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
123Exported constants:
124
125  True
126  False
127
128Exported 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
139import re, string, time, operator
140
141from types import *
142import socket
143import errno
144import httplib
145try:
146    import gzip
147except ImportError:
148    gzip = None #python can be built without zlib/gzip support
149
150# --------------------------------------------------------------------
151# Internal stuff
152
153try:
154    unicode
155except NameError:
156    unicode = None # unicode support not available
157
158try:
159    import datetime
160except ImportError:
161    datetime = None
162
163try:
164    _bool_is_builtin = False.__class__.__name__ == "bool"
165except NameError:
166    _bool_is_builtin = 0
167
168def _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
174def escape(s, replace=string.replace):
175    s = replace(s, "&", "&")
176    s = replace(s, "<", "&lt;")
177    return replace(s, ">", "&gt;",)
178
179if 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
186else:
187    def _stringify(string):
188        return string
189
190__version__ = "1.0.1"
191
192# xmlrpc integer limits
193MAXINT =  2L**31-1
194MININT = -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
201PARSE_ERROR       = -32700
202SERVER_ERROR      = -32600
203APPLICATION_ERROR = -32500
204SYSTEM_ERROR      = -32400
205TRANSPORT_ERROR   = -32300
206
207# Specific errors
208NOT_WELLFORMED_ERROR  = -32700
209UNSUPPORTED_ENCODING  = -32701
210INVALID_ENCODING_CHAR = -32702
211INVALID_XMLRPC        = -32600
212METHOD_NOT_FOUND      = -32601
213INVALID_METHOD_PARAMS = -32602
214INTERNAL_ERROR        = -32603
215
216# --------------------------------------------------------------------
217# Exceptions
218
219##
220# Base class for all kinds of client-side errors.
221
222class 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
237class 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
256class 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
269class 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
292from sys import modules
293mod_dict = modules[__name__].__dict__
294if _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
299else:
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
347del 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
362def _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
376class 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
460def _datetime(data):
461    # decode xml element contents into a DateTime structure.
462    value = DateTime()
463    value.decode(data)
464    return value
465
466def _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
476import base64
477try:
478    import cStringIO as StringIO
479except ImportError:
480    import StringIO
481
482class 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
509def _binary(data):
510    # decode xml element contents into a Binary structure
511    value = Binary()
512    value.decode(data)
513    return value
514
515WRAPPERS = (DateTime, Binary)
516if not _bool_is_builtin:
517    WRAPPERS = WRAPPERS + (Boolean,)
518
519# --------------------------------------------------------------------
520# XML parsers
521
522try:
523    # optional xmlrpclib accelerator
524    import _xmlrpclib
525    FastParser = _xmlrpclib.Parser
526    FastUnmarshaller = _xmlrpclib.Unmarshaller
527except (AttributeError, ImportError):
528    FastParser = FastUnmarshaller = None
529
530try:
531    import _xmlrpclib
532    FastMarshaller = _xmlrpclib.Marshaller
533except (AttributeError, ImportError):
534    FastMarshaller = None
535
536try:
537    from xml.parsers import expat
538    if not hasattr(expat, "ParserCreate"):
539        raise ImportError
540except ImportError:
541    ExpatParser = None # expat not available
542else:
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
563class 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
590class 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
764class 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
935class _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
946class MultiCallIterator:
947    """Iterates over the results of a multicall. Exceptions are
948    raised 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
963class 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
1008def 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
1046def 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
1125def 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
1147def 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
1171def 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
1195class 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
1215class _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
1232class 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
1478class 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
1516class 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        if isinstance(uri, unicode):
1543            uri = uri.encode('ISO-8859-1')
1544
1545        # get the url
1546        import urllib
1547        type, uri = urllib.splittype(uri)
1548        if type not in ("http", "https"):
1549            raise IOError, "unsupported XML-RPC protocol"
1550        self.__host, self.__handler = urllib.splithost(uri)
1551        if not self.__handler:
1552            self.__handler = "/RPC2"
1553
1554        if transport is None:
1555            if type == "https":
1556                transport = SafeTransport(use_datetime=use_datetime)
1557            else:
1558                transport = Transport(use_datetime=use_datetime)
1559        self.__transport = transport
1560
1561        self.__encoding = encoding
1562        self.__verbose = verbose
1563        self.__allow_none = allow_none
1564
1565    def __close(self):
1566        self.__transport.close()
1567
1568    def __request(self, methodname, params):
1569        # call a method on the remote server
1570
1571        request = dumps(params, methodname, encoding=self.__encoding,
1572                        allow_none=self.__allow_none)
1573
1574        response = self.__transport.request(
1575            self.__host,
1576            self.__handler,
1577            request,
1578            verbose=self.__verbose
1579            )
1580
1581        if len(response) == 1:
1582            response = response[0]
1583
1584        return response
1585
1586    def __repr__(self):
1587        return (
1588            "<ServerProxy for %s%s>" %
1589            (self.__host, self.__handler)
1590            )
1591
1592    __str__ = __repr__
1593
1594    def __getattr__(self, name):
1595        # magic method dispatcher
1596        return _Method(self.__request, name)
1597
1598    # note: to call a remote object with an non-standard name, use
1599    # result getattr(server, "strange-python-name")(args)
1600
1601    def __call__(self, attr):
1602        """A workaround to get special attributes on the ServerProxy
1603           without interfering with the magic __getattr__
1604        """
1605        if attr == "close":
1606            return self.__close
1607        elif attr == "transport":
1608            return self.__transport
1609        raise AttributeError("Attribute %r not found" % (attr,))
1610
1611# compatibility
1612
1613Server = ServerProxy
1614
1615# --------------------------------------------------------------------
1616# test code
1617
1618if __name__ == "__main__":
1619
1620    # simple test program (from the XML-RPC specification)
1621
1622    # server = ServerProxy("http://localhost:8000") # local server
1623    server = ServerProxy("http://time.xmlrpc.com/RPC2")
1624
1625    print server
1626
1627    try:
1628        print server.currentTime.getCurrentTime()
1629    except Error, v:
1630        print "ERROR", v
1631
1632    multi = MultiCall(server)
1633    multi.currentTime.getCurrentTime()
1634    multi.currentTime.getCurrentTime()
1635    try:
1636        for response in multi():
1637            print response
1638    except Error, v:
1639        print "ERROR", v
1640